static、 静态导入、成员变量的初始化、单例模式、final 常量(Content)、嵌套类、局部类、抽象类、接口、Lambda、方法引用

static

static 常用来修饰类的成员:成员变量、方法、嵌套类

成员变量

  • static修饰:类变量、成员变量、静态字段
    • 在程序中只占用一段固定的内存(存储在方法区),所有对象共享
    • 可以通过实例、类访问 (一般用类名访问和修改,不建议用实例修改),但是不能通过类名访问实例变量和实例方法
  • 没有被static修饰:实例 变量
    • 每个实例都有一份内存
java 复制代码
package com.hxw.demo4;

public class Person {
    public static  int count = 0;
    public int age;
}
java 复制代码
package com.hxw.demo4;
public class Main {
    public static void main(String[] args) {
        Person.count = 20;

        Person p1 = new Person();
        p1.age = 20;

        System.out.println(p1.count);

        Person p2 = new Person();
        p2.age = 30;
        System.out.println(p2.count);
        
    }
}

控制台输出

java 复制代码
20
20

方法

  • static修饰:类方法、静态方法
    • 可以通过实例、类访问
    • 内部不可以使用this
    • 可以直接访问类变量、类方法
    • 不可以直接访问实例变量和实例方法
  • 没有被static修饰:实例方法
    • 只能通过实例访问,不可以通过类访问
    • 内部可以使用this
    • 可以直接访问实例变量和实例方法
java 复制代码
package com.hxw.demo4;

public class Person {
    public static int count = 0;
    public int age;

    // 实例方法
    public void run1(){
        // 实例方法中可以使用this调用其他实例方法
        this.run2();
        //实例方法可以直接访问类变量和类方法
        System.out.println(count);
        test1();
        System.out.println(this.age);
    }

    public void run2(){
        System.out.println("void run2");
    }

    public static void test1(){
        System.out.println("static void test1");
        test2();
        System.out.println(count);
    }
    public static void test2(){
        System.out.println("static void test2");
    }
}
java 复制代码
package com.hxw.demo4;

public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        person.age = 18;
        person.run1();

        Person.test1();
    }
}

总结:

静态导入

使用了静态导入之后,就可以省略类名来访问静态成员(成员变量、方法、嵌套类)

静态导入的经典应用

为了避免每一次都是用Math.PI,可以考虑使用静态导入,其他的Math方法也是一样的道理。

成员变量的初始化

  • 编译器会自动为未初始化的成员变量设置初始值
  • 如何手动给实例变量提供初始值
    • 在声明中:定义变量的时候直接给其设置初始值
    • 在构造方法中:在构造方法内部利用this进行初始化
    • 在初始化中 :这个情况看下面代码,了解一下,
java 复制代码
package com.hxw.dmeo6;

public class Person {
    public static int count;
    public int age = 1; //申明中
    public Person(){
        age = 20;// 在构造方法中,但是先执行初始化中的代码
    }

    //在初始化中
    {
        age = 18;
    }
}

一般很少使用初始化代码,方法如下 公共初始化代码一般放到参数最多的构造方法中

java 复制代码
package com.hxw.dmeo6;

public class Person {
    public static int count;
    public int age = 1; //申明中

    public Person(){
        this(0, 0);
    }
    public Person(int age){
        this(age, 0);
    }
    public Person(int age, int no){
        // 公共初始化代码一般放到参数最多的构造方法中
    }

//    //在初始化中
//    {
//        age = 18;
//    }
}
  • 如何手动给类变量提供初始值
    • 在声明中
    • 在静态初始化代码块中:当一个类初始化的时候 (也就是一个类第一次被主动使用时 ,JVM会对类进行初始化),就会执行静态初始化代码块,且只会执行一次。
      • 可以有多个静态初始化块,按照在源码中出现的顺序被执行

初始化块和静态初始化块

这里实际的执行顺序和自己想的不一样,要先初始化完父类,再初始化子类

单例模式

如果一个类设计为单例模式,那么程序运行过程,这个类只能创建一个实例

  • 饿汉单例模式:一开始就创建了对象
  • 懒汉单例模式:等到该创建对象的时候创建对象,懒汉单例模式会有线程安全问题如果三个对象同时构建实例,那么会创建多个对象,但是最终创建的才会赋值给instance,所以总的说来还是单例模式

Rocket.class

java 复制代码
package com.hxw.demo7;

/**
 * 饿汉式单例模式:一上来就new了一个对象
 */
//public class Rocket {
//    //私有的静态(唯一内存)实例变量
//    private static Rocket instance = new Rocket();
//
//    // 构造方法私有化 在Main中就不能new Rocket()
//    private Rocket(){
//
//    }
//
//    //公共的静态方法,返回唯一的静态实例
//    public static Rocket getInstance(){
//        return instance;
//    }
//}

/**
 * 懒汉式单例模式
 */

public class Rocket {
    public static Rocket instace = null;
    private Rocket(){

    }
    public static Rocket getInstance(){
        //用到的时候再new对象
        if(instace == null){
            instace = new Rocket();
        }
        return instace;
    }
}

Main.class

java 复制代码
package com.hxw.demo7;

public class Main {
    public static void main(String[] args) {
        Rocket r1 = Rocket.getInstance();
        Rocket r2 = Rocket.getInstance();
        Rocket r3 = Rocket.getInstance();

        //r1 r2 r3 都是同一个instance

        System.out.println(r1 == r2);

        System.out.println(r2 == r3);
    }
}

控制台输出

true
true

final和常量

  • final修饰的类不能被继承
  • final修饰的成员方法不能被重写
  • final修饰的变量,只能进行一次赋值,而且需要初始化,如果定义的时候没有初始化,那么在构造函数中一定要初始化



常量(Content)

  • 常量的写法: public static final double PI = 3.14159265358979323846;,一般名字全部用大写
  • 如果将基本类型或字符串定义为常量,并且在编译时就能确定值,编译器就会使用常量值代替各处的常量名(类似于C语言的宏替换),这样可以减少访问内存的次数,提高代码运行的效率

嵌套类

内部类(Inner Class,非静态嵌套类)

  • 嵌套类:定义在另一个类中的类
  • 外部类:在嵌套类外层的类
  • 顶级类:最外层的外部类
  • 内部类 :没有被static修饰的嵌套类,非静态嵌套类。
    • 跟实例变量、实例方法一样,内部类与外部类的实例相关联**,必须先创建外部类实例,然后再用外部类实例创建内部类实例**。
    • 内部类不能定义除了除编译时常量以外的任何static成员
    • 内部类可以直接访问外部类中的所有成员(即使被申明为static)因为先创建了外部类实例,再创建内部类实例。
    • 外部类可以直接访问内部类实例的成员变量、方法
      外部类、内部类如下
java 复制代码
package com.hxw.demo9;

public class OuterClass {
    // 静态嵌套类
    static class StaticNestedClass {

    }

    // 非静态嵌套类(内部类)
    class InnerClass{

    }
	
	class A{
        class B{
          class C{
              
          }
        }
    }

}

对应的内存图如下

静态嵌套类

  • 静态嵌套类:被static修饰的嵌套类
  • 静态嵌套类在行为上就是一个顶级类,只是定义的代码写在了另一个类中
  • 对比一般的顶级类,静态嵌套类多了一些特殊权限
    • 可以访问外部类中的成员(即使被申明为static)


什么情况使用嵌套类

情况一:如果类A只用在类C内部,可以考虑将类A嵌套到类C内部

  • 封装性更好
  • 程序包更加简化
  • 增强可读性、维护性

情况二:如果类A需要经常访问类C的非公共成员,可以考虑将类A嵌套到类C中

  • 另外也可以根据需要将类A隐藏起来,不对外暴露

情况三:如果需要经常访问非公共的实例成员(而且需要现有外层,后有内层的关系,例如现有公司Company 后有员工Employee),设计成内部类(非静态嵌套类),否则设计成静态嵌套类

  • 如果必须现有C实例,才能创建A实例,那么可以将A设计为C的内部类

情况一案例:

局部类

  • 局部类:定义在代码块(包含{},可以在里面写代码的块)中的类(可以定义在方法中,for循环中,if语句中)
  • 局部类不能定除编译时常量以外的任何static成员
  • 局部类只能访问final或者有效final(只赋值一次,没有被第二次赋值)的局部变量
  • 局部类可以直接访问外部类的所有成员
    • 局部类只有定义在实例相关的代码块中,才能直接访问外部类中的实例成员(实例变量、实例方法)

由于局部类是定义在代码块中的,所以其作用域也只局限于代码块中,因此,什么时候会用到局部类呢?当一个类是在一个代码块中使用的时候

java 复制代码
package com.hxw.demo10;

public class Person {
    static {
        class A{

        }
    }
    {
        int age = 10;
        class A{
            
        }
    }
    public void test(){
        for (int i = 0; i < 10; i++) {
            class A{

            }
        }

        if(true){
            class A{

            }
        }else{
            class A{

            }
        }

        {
            class A{

            }
        }
    }
}

抽象类

抽象方法(Abstract Method)

  • 抽象方法:被abstract修饰的方法
    • 只有方法申明,没有方法实现
    • 不能是private权限(因为定义抽象方法的目的就是为了让子类去实现
    • 只能是实例方法,而不能是类方法
    • 只能定义在抽象类,接口中
java 复制代码
package com.hxw.demo11;

public abstract class Main {
    public static void main(String[] args) {

    }
    public abstract void test();
}

抽象类

  • 抽象类:被abstract修饰的类
    • 可以定义抽象方法
    • 不能实例化,但是可以自定义构造方法
    • 子类必须实现抽象父类中的所有抽象方法(除非子类也是一个抽象类)
    • 可以向非抽象类一样定义成员变量、常量、嵌套类型、初始化块、非抽象方法等,也就是,抽象类也可以完全不定义抽象方法

常见使用场景

  • 抽取子类的公共实现到抽象父类中,要求子类必须要单独实现的去定义成抽象方法

接口(Interface)

联想到日常笔记本电脑的接口,不同厂商的USB长得都是一样的,这是他们遵守相同的标准

  • API(Application Programming Interface

    • 应用编程接口,提供给开发者调用的一组功能(无需提供源码)
  • java中的接口

    • 一系列方法申明的集合
    • 用来定义规范、标准

抽象类(abstract )是通过子类继承来实现其抽象方法,接口(Interface)是通过其他类实现该接口来实现其抽象方法

家教案例引入接口概念

有一个小孩,需要应聘家教,应聘要求:

  • 会教授编程
  • 会教踢足球
    涉及的人群可能有:在校的学生,在校的老师、出租车司机

首先看没有使用接口:

  • 定义一个孩子类
java 复制代码
package com.hxw.demo12;

public class Child {
    private String name;

    private Student jiajiao;
    public Child(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Student getJiajiao() {
        return jiajiao;
    }

    public void setJiajiao(Student jiajiao) {
        this.jiajiao = jiajiao;
    }

    public void study(){
        jiajiao.jiaoBianCheng(this);
        jiajiao.jiaoZuQiu(this);
    }

}
  • 定义一个学生类
java 复制代码
package com.hxw.demo12;

public class Student {
    public void jiaoBianCheng(Child child){
        System.out.println("Student教" + child.getName() + "编程");

    }

    public void jiaoZuQiu(Child child){
        System.out.println("Student教" + child.getName() + "足球");
    }


}
  • 主函数
java 复制代码
package com.hxw.demo12;

public class Main {
    public static void main(String[] args) {
        Child child = new Child("Jack");
        child.setJiajiao(new Student());
        child.study();
    }
}

如果希望老师教学生的话,按照这种方式就需要再创建一个Teacher类...如果教课的人身份众多,就会创建多个类,要单独修改Child类中的jiajiao属性,以及本类中的方法,这样很繁琐,但是这些类本质上都是实现了两个方法,招人的要求是满足这两个要求,但是对于这个人其他的特征,其实不用了解。此时,就可以将这两个行为定义到接口当中,各种身份的家教去实现这个接口即可。

  • 创建JiaJiaoable接口,将原来Student中的方法复制过去,并改写为抽象方法。
java 复制代码
package com.hxw.demo12;

public interface JiaJiaoable {
    public abstract void jiaoBianCheng(Child child);

    public abstract void jiaoZuQiu(Child child);
}

Student类、Teacher类分别实现JiaJiaoable接口中的方法

java 复制代码
package com.hxw.demo12;

public class Student implements JiaJiaoable{
    @Override
    public void jiaoBianCheng(Child child) {
        System.out.println("Student教" + child.getName() + "biancheng");
    }

    @Override
    public void jiaoZuQiu(Child child) {
        System.out.println("Student教" + child.getName() + "zuqiu");

    }
}
java 复制代码
package com.hxw.demo12;

public class Teacher implements JiaJiaoable{

    @Override
    public void jiaoBianCheng(Child child) {
        System.out.println("Teacher教" + child.getName() + "biancheng");

    }

    @Override
    public void jiaoZuQiu(Child child) {
        System.out.println("Teacher教" + child.getName() + "zuqiu");
    }
}

修改孩子类的家教属性为借口类型

java 复制代码
package com.hxw.demo12;

public class Child {
    private String name;

    private JiaJiaoable jiajiao;
    public Child(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public JiaJiaoable getJiajiao() {
        return jiajiao;
    }

    public void setJiajiao(JiaJiaoable jiajiao) {
        this.jiajiao = jiajiao;
    }

    public void study(){
        jiajiao.jiaoBianCheng(this);
        jiajiao.jiaoZuQiu(this);
    }

}

调用Main方法:

java 复制代码
package com.hxw.demo12;

public class Main {
    public static void main(String[] args) {
        Child child = new Child("Jack");
        child.setJiajiao(new Student());
        child.study();

        child.setJiajiao(new Teacher());
        child.study();
    }
}

控制台输出

Student教Jackbiancheng
Student教Jackzuqiu
Teacher教Jackbiancheng
Teacher教Jackzuqiu

接口中可以定义的内容

  • 可以定义:抽象方法、常量、嵌套类型,从java8开始定义
    • 上述可以定义的内容都是隐式public的,因此可以省略public关键字
    • 从java9开始可以定义private方法
  • 常量可以省略staticfinal
    • 接口中没有成员变量,写出来就是常量
  • 抽象方法可以省略abstract
  • 不能自定义构造方法、不能定义初始化块、不能实例化

Lambda

  • Lambda只能访问finial或者有效finial的局部变量
  • Lambda没有引入新的作用域

定义一个OuterClass

java 复制代码
package com.hxw.demo13;

public class OuterClass {
    private int age = 1;

    public class InnerClass{
        private int age = 2;
        void inner(){
            Testable t = v -> {
                System.out.println(v);
                System.out.println(age);
                System.out.println(this.age);
                System.out.println(InnerClass.this.age);
                System.out.println(OuterClass.this.age);
            };
            t.test(3);
        }
    }

    public static void main(String[] args) {
        new OuterClass().new InnerClass().inner();
    }
}

方法引用

相关推荐
程序猿阿伟5 分钟前
《C++中的魔法:实现类似 Python 的装饰器模式》
java·c++·装饰器模式
Mr. zhihao7 分钟前
装饰器模式详解:动态扩展对象功能的优雅解决方案
java·开发语言·装饰器模式
zyhomepage7 分钟前
科技的成就(六十四)
开发语言·人工智能·科技·算法·内容运营
Ethan Wilson13 分钟前
C++/QT可用的websocket库
开发语言·c++·websocket
2401_8576009513 分钟前
商场应急管理:SpringBoot技术解决方案
java·spring boot·后端
想做白天梦25 分钟前
多级反馈队列
java·windows·算法
潇雷27 分钟前
算法Day12|226-翻转二叉树;101-对称二叉树;104-二叉树最大深度;111-二叉树最小深度
java·算法·leetcode
一颗甜苞谷34 分钟前
开源一套基于若依的wms仓库管理系统,支持lodop和网页打印入库单、出库单的源码
java·开源
半夏之沫36 分钟前
✨最新金九银十✨大厂后端面经✨
java·后端·面试