• 【JAVA】07 面向对象(高级)类变量、类方法、代码块、final、抽象类、接口、内部类


    教程:b站韩顺平

    一、类变量

    1. 类变量名也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量

    2. 定义:

      访问修饰符 static 数据类型 变量名;
      
      • 1
    3. 访问:

      类名.类变量名(推荐)
      
      对象名.类变量名 //静态变量的访问修饰符的访问权限和范围 和 普通属性 是一样的
      
      • 1
      • 2
      • 3
      • static变量是对象共享,不管static变量在哪里(堆/方法区),共识:
      • static变量是同一个类所有对象共享
      • static变量,在类加载的时候就生成了
    4. 注意事项和细节讨论:

      • 实用类变量的时机:让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量)
      • 类变量与实例变量(普通属性)的区别:类变量是该类的所有对象共享的,而实例变量是每个对象独享的
      • 加上static称为类变量或者静态变量,否则称为实例变量/普通变量/非静态变量
      • 实例变量不能通过 类名.类变量名 访问
      • 类变量是在类加载时就初始化了,即使没有创建对象,只要类加载了,就可以使用类变量了
      • 类变量的生命是随类加载开始,随类消亡销毁

    二、类方法

    2.1 基本概念

    1. 类方法,即静态方法

    2. 定义:

      访问修饰符 static 数据返回类型 方法名(){}
      
      • 1
    3. 调用:

      类名.类方法名()
      对象名.类方法名() //前提是满足访问修饰符的访问权限和范围
      
      • 1
      • 2
    4. 类方法经典使用场景:
      当方法中不涉及到任何和对象相关的成员,则可以将该方法设计成静态方法,提高开发效率
      例如工具类中的方法:Math类、Array类等

    5. 注意事项:

      • 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区,类方法中无this的参数,普通方法中隐含着this的参数
      • 类方法可以通过类名调用,也可以通过对象名调用
      • 普通方法和对象有关,通过对象名调用,比如 对象名.方法名(参数),不能通过类名调用
      • 类方法中不允许使用和对象有关的关键字,如 this,super,普通方法可以使用
      • 类方法(静态方法)中 只能访问静态变量 或静态方法
      • 普通成员方法,既可以访问 普通变量(方法),又可以访问静态变量(方法)
    6. 小结:

      • 静态方法:只能访问静态的成员
      • 非静态的方法:可以访问静态成员和非静态成员

    2.2 main方法

    1. main()方法的形式

      public static void main(String[] args){}
      
      • 1

    在这里插入图片描述

    1. main方法时虚拟机调用
      • java虚拟机需要调用类的main方法,所以该方法的访问权限必须是public
      • java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
      • 该方法接受String类型的数组参数,该数组中保存执行java命令时传递给运行的类的参数
      • 可以直接使用name
      • 静态方法main,可以访问本类的静态成员
      • 静态方法main不可以访问本类的非静态成员
      • 静态方法main 要访问本类的非静态成员,需要先创建对象, 再调用即可
    2. 理解main方法语法
      在main()方法中 ,可以直接调用main方法所在类的静态方法或静态属性
      但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员

    三、代码块

    1. 代码化块又成为初始化块,属于类中的成员【即 是类的一部分】,类似于方法,将逻辑语句封装在方法体中,通过{}包围

    2. 和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不能通过对象或类显示调用,而是加载类时,或创建对象是隐式调用

      基本语法
      [修饰符]{
          代码
      };
      
      • 1
      • 2
      • 3
      • 4
    3. tips: 修饰符 可选,要写的话,也只能写static

    4. 代码块分为两类:
      - 使用static:静态代码块
      - 没有static修饰:普通代码块

      • 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断)
      • ;号可以写上,也可以省略
    5. 创建一个对象时:

      • 调用 静态代码块 和 静态属性 初始化:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按照他们定义的顺序调用
      • 调用 普通代码块 和 普通属性 初始化:普通代码块和普通属性初始化调用的优先级一样,如果有多个代码块和多个普通属性初始化,则按照定义顺序调用
        调用构造方法
    6. 构造方法(构造器)的最前面其实隐含了super()和调用普通代码块,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于 构造器和普通代码执行的

    7. 创建一个子类时:

      • 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
      • 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
      • 父类的构造方法
      • 子类的普通代码和普通属性初始化(优先级一样,按定义顺序执行)
      • 子类的构造方法
    8. 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员

    四、final关键字

    1. final:可以修饰类、属性
    2. 应用场景:
      • 当不希望类被继承时,可以用final修饰
      • 当不希望父类的某个方法被子类覆盖/重写时,可以使用final关键字修饰
      • 当不希望类的某个属性的值被修改,可以使用final修饰
      public final double TAX_RATE=0.08;
      
      • 1
      • 当不希望某个局部变量被修改,可以使用final修饰
    final double TAX_RATE-0.08;
    
    • 1
    1. 细节
      • final修饰的属性又称常量,一般用XX_XX_XX命名
      • final修饰的属性在定义时,必须赋初值,并且以后不能在修改,赋值可以在如下位置之一【选择一个位置赋初值】
        定义时:
        public final double TX_RATE=0.08;
        在构造器中
        在代码块中
        如果final修饰的属性是静态的,则初始化的位置只能是
        定义时
        在静态代码块 不能在构造器中赋值
        final类不能继承,但是可以实例化对象
        如果类不是final类,但是含有final方法,则该方法虽然不能重写,但可以被继承
        一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法了
        final不能修饰构造方法(构造器)
        final和static往往搭配使用,效率更高,底层编译器做了优化处理
        包装类(Integer,Double,Float,Boolean等都是final),String也是final类

    五、抽象类

    1. 当父类的某些方法,需要声明,但是又不确定如何是实现时,可以将其声明为抽象方法,那么这个类就是抽象类

    2. 抽象类:
      a. 不能被实例化
      b. 可以有任意成员,比如非抽象方法、构造器、静态属性等
      c. 多用于框架和设计模式
      d. 抽象类的价值更多作用在于设计,是设计者设计好后,让子类继承并实现抽象类
      e. 如果一个类继承了抽象类,则它需要实现抽象类的所有抽象方法,除非他自己也声明为一个abstract类
      f. 抽象类不一定要包含abstract方法
      g. 一旦类包含abstract方法,则该类必须声明为abstract
      h. abstract只能修饰类和方法,不能修饰属性和其他

      访问修饰符 abstract 类名{
          
      }
      
      • 1
      • 2
      • 3
    3. 抽象方法:没有方法体

      访问修饰符 abstract 返回类型 方法名(参数列表);
      
      • 1
    4. 抽象方法不能使用 private、final、static 修饰, 因为这些关键字和重写相违背

    5. 应用:模板设计模式

    六、接口

    1. 接口解释给出一些没有实现的方法,封装到一起,到某各类要使用时,再根据具体情况把这些方法写出来
    //1. 声明接口
    interface 接口名{
        //属性
        //方法(1.抽象方法2.默认实现方法3.静态方法)
    }
    //2. 类使用接口,则必须实现接口中的方法
    class 类名 implements 接口{
        //自己属性
        //自己方法
        //必须实现的接口的抽象方法            
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 在jdk7.0之前,接口中的所有方法都没有方法体,都是抽象方法
    2. 在jdk8.0之后,接口类可以有静态方法,默认方法,也就是说接口中可以有具体方法实现,需要使用 default 关键字
    default public void ok(){
        
    }
    
    • 1
    • 2
    • 3
    1. 案例1
      UsbInterface.java
    public interface UsbInterface {
        public void start();
        public void stop();
    }
    
    • 1
    • 2
    • 3
    • 4

    Camera.java

    public class Camera implements UsbInterface{
        public void start(){
            System.out.println("相机开始工作");
        }
        public void stop(){
            System.out.println("相机停止工作");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Phone.java

    public class Phone implements UsbInterface{
        public void start(){
            System.out.println("手机开始工作");
        }
        public void stop(){
            System.out.println("手机停止工作");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Computer.java

    public class Computer {
        public void work(UsbInterface usbInterface){
            usbInterface.start();
            usbInterface.stop();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    InterfaceExercise.java

    public class InterfaceExercise {
        public static void main(String[] args){
            Camera camera = new Camera();
            Computer computer = new Computer();
            computer.work(camera); //把相机接入电脑
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 案例二
      InterfaceExercise02.java
    interface A {
        int x=0;
    }
    class B{
        int x=1;
    }
    class C extends B implements A{
        public void pX(){
            System.out.println(super.x);
            System.out.println(A.x);
        }
        public static void main(String[] args){
            //因为pX不是静态类,所以调用时需要创建对象
            new C().pX();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 接口的注意事项和细节
      a. 接口不能被实例化
      b. 接口中所有的方法时public方法,接口中抽象方法,可以不用abstract修饰

      void aaa();
      //实际上是 abstract void aaa();
      
      • 1
      • 2

      c. 一个普通类实现接口,就必须将该接口的所有方法都实现
      d. 抽象类实现接口,可以不用实现接口的方法
      e. 一个类同时可以实现多个接口
      f. 接口中的属性,只能是final,而且是

      	 public static final 修饰符
      	// public static final int a=1;(必须初始化)
      
      • 1
      • 2

      g. 一个接口不能继承其他的类,但是可以继承多个别的接口

      interface A extends B,C{}
      
      • 1

      h. 接口的修饰符 只能是public和默认,这点和类的修饰符是一样的

    2. 实现接口 vs 继承类
      a. 接口和继承解决的问题不同
      ⅰ. 继承:解决代码复用性和可维护性
      ⅱ. 接口:设计好各种规范(方法),让其他类去实现这些方法
      b. 接口比继承更加灵活
      ⅰ. 继承:is-a
      ⅱ. 接口:like-a
      c. 接口在一定程度上实现代码解耦【接口规范性+动态绑定】

    3. 接口的多态特性
      a. 多态参数:在USB案例中,接口既可以接收手机,又可以接收相机(接口引用可以指向实现了接口的类的对象)
      b. 多态数组

      Usb usbs[]=new usb[2];
      usbs[0]=new Phone();
      usbs[1]=new Camera();
      for(int i=0;i<usbs.length;i++){
      	usbs[i].start();
          usbs[i].stop();
      }
      if(usbs[i] instanceof Phone){
      	((Phone)usbs[i]).call();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

      c. 多态传递

      interfce AInterface{
      	void f1();
      }
      class B implements AInterface{
      	@Override
          public void f1(){
              System.out.println("f1!");
          }
      }
      class C extends B{}
      
      //....
      C c = new C();
      AInterface af = c;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

    七、内部类

    一个类的内部又完整地嵌套了另一个类结构,被嵌套的类为内部类,嵌套其他类的类称为外部类

    	class Outer{	//外部类
    		class Inner{	//内部类
    	    
    	    }
    	}
    	
    	class Other{	//外部其他类
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    7.1 定义在外部类局部位置上(如方法内)

    7.1.1 局部内部类(有类名)

    	public class InnerClass {
    	    public static void main(){
    	        Outer outer = new Outer();
    	        outer.m1();
    	    }
    	}
    	
    	class Outer{
    	    private int n1=100;
    	    private void m2(){}//私有方法
    	    public void m1(){
    	        final class Inner{
    	            public void f1(){
    	                System.out.println("n1="+n1);
    	                m2();
    	            }
    	        }
    	        Inner inner=new Inner();
    	        inner.f1();
    	    }
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    7.1.2 匿名内部类(没有类名)

    1. 特点:
      a. 本质是类
      b. 内部类
      c. 该类没有名字
      d. 同时还是一个对象

    2. 说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名

    3. 基本语法:

      new 类或接口(参数列表){
      	类体
      };
      
      • 1
      • 2
      • 3
    4. 案例1-基于接口的匿名内部类:

      public class AnonymousClass {
          public static void main(String[] args){
              IA tiger = new Tiger();
              tiger.cry();
          }
      }
      
      interface IA{
          public void cry();
      }
      
      class Tiger implements IA{
          public void cry(){
              System.out.println("Tiger cry");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16

      将上面代码转变为匿名内部类的形式:

      public class AnonymousClass {
          public static void main(String[] args){
              IA tiger = new IA(){
                  public void cry(){
                      System.out.println("老虎叫唤");
                  }
              };
              tiger.cry();
              System.out.println(tiger.getClass());
          }
      }
      
      interface IA{
          public void cry();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

      ● tiger编译类型:tiger
      ● tiger运行类型:匿名内部类 AnonymousClass$1(系统分配)

      class AnonymousClass$1 implements IA{
      	@Override
          public void cry(){
              System.out.println("老虎叫唤");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      ● jdk在底层创建匿名内部类 AnonymousClass$1,立马就创建了AnonymousClass$1实例,并把地址返回给tiger
      ● 匿名内部类使用一次就不能再使用了,但是可以被多次调用

    5. 案例2-基于类的匿名内部类

      public class AnonymousClass {
          public static void main(String[] args){
              Father father1 = new Father("father1");
              father1.test();
              Father father2 = new Father("father2"){
                  @Override
                  public void test(){
                      System.out.println("匿名内部类重写了test方法");
                  }
              };
              father2.test();
              System.out.println("father2的运行类型:"+father2.getClass());
          }
      }
      
      class Father{
          private String name;
          public Father(String name){
              this.name=name;
          }
          public void test(){
              System.out.println("Father\t"+this.name+"\ttest()");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24

      ● 系统创建AnonymousClass$1,立马就创建了AnonymousClass$1实例,并把地址返回给father2

      class AnonymousClass$1 extends Father{
      	@Override
          public void test(){
              System.out.println("匿名内部类重写了test方法");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    6. 案例-基于抽象类的匿名内部类

      abstract class Animal{
      	abstract void eat();
      }
      Animal animal = new Animal(){
      	@Overrider
          void eat(){ //因为抽象类Animal,所以必须实现eat()
              System.out.println("小狗吃骨头");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    7. 使用细节
      ● 可以直接访问外部类的所有成员,包含私有的
      ● 不能添加访问修饰符,因为它的地位就是一个局部变量
      ● 作用域:仅仅在定义它的方法或代码块中
      ● 匿名内部类 访问——>外部类成员【直接访问】
      ● 外部其他类 不能访问——>匿名内部类【应为匿名内部类地位=局部变量】
      ● 外部类 和 内部类 成员重名是,内部类访问遵循就近原则,访问外部类成员: 外部类名.this.成员
      ● 练习

      public class InnerClassExercise {
          public static void main(String[] args){
              Bell getup = new Bell() {
                  @Override
                  public void Ring() {
                      System.out.println("getup!!");
                  }
              };
              Bell gotoClass = new Bell() {
                  @Override
                  public void Ring() {
                      System.out.println("goutoClass!!");
                  }
              };
              CellPhone cellPhone = new CellPhone();
              cellPhone.alarmclock(getup);
              cellPhone.alarmclock(gotoClass);
          }
      }
      
      
      interface Bell{
          void Ring();
      }
      
      class CellPhone{
          public void alarmclock(Bell bell){
              System.out.println(bell.getClass());
              bell.Ring();//方法是动态绑定机制
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31

    7.2 定义在外部类的成员位置上

    7.2.1 成员内部类(没用static修饰)

    1. 成员内部类是定义在外部类的成员位置,并没有static修饰

    2. 可以直接访问外部类的所有成员,包括私有的

    3. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员

    4. 作用域:和外部类的其他成员一样,为整个类体,在外部类的成员方法中创建成员内部类对象,再调用方法

    5. 成员内部类 访问——>外部成员【访问方式:直接访问】

    6. 外部类 访问——>内部成员【创建对象,再访问】

    7. 外部其他类 访问——成员内部类
      a. 法一: 直接创建

      Outer.Inner inner = outer.new Inner();
      
      • 1

      b. 法二:在外部类中,编写一个方法返回成员内部类对象

      	Outer.Inner innerInstance = outer.getInnerInstance();
      	innerInstance.say();
      
      • 1
      • 2
    8. 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类成员,则可以使用(外部类名.this.成员)去访问

      class A{
      	private int n1=10;
          private static String name = "name";
          class B{
              private int n1=20;
              public void sat(){
                  System.out.println("A n1"+A.this.n1+"B n1"+n1);
              }
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

    7.2.2 静态内部类(使用static修饰)

    1. 说明:静态内部类是定义在外部类的成员位置,有static修饰

    2. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员

    3. 可以添加任意访问修饰符public、protected、默认、private,因为其地位就是一个成员

    4. 作用域:同其他的成员,为整个类体

    5. 静态内部类 访问——外部类(如静态属性)【访问方式:直接访问所有静态成员】

    6. 外部类 访问——>静态内部类【访问方式:创建对象,再访问】

      class A{
      	private int n1 =10;
          private static String name = "tom";
      
          private static void cry(){
              System.out.println("cry");
          }
          static class B{
              public void say(){
                  System.out.println(name);
                  cry();
              }
          }
          public void test(){
              B b = new b();
              b.say();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    7. 外部其他类 访问——静态内部类
      a. 因为静态内部类可以通过类名直接访问(前提:满足访问权限)

      B b = new A.B();//这里就不会创建一个A对象实例
      b.say();
      
      • 1
      • 2

      b. 通过一个方法来获取一个静态内部类的对象实例

      public class StaticClass{
      	public static void main(String[] args){
              A a = new A();
              A.B b = a.getB();
              b.cry();
          }
      }
      
      class A{
      	static class B{
              public void say(){
                  System.out.println(name);
                  cry();
              }
          }
          public B getB(){
              return new B();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

      若修改为静态方法,会更方便,就不会创建内部类对象了

      public class StaticClass{
      	public static void main(String[] args){
              A.B b = A.getB();
              b.cry();
          }
      }
      
      class A{
      	static class B{
              public void say(){
                  System.out.println(name);
                  cry();
              }
          }
          public static B getB(){
              return new B();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    8. 如果外部类和静态类内部类的成员重名是,静态内部类访问时,遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问

      //法一,注意没有生成A类对象实例
      B b = new A.B();
      b.say();
      //法二:如果访问内部类的静态方法,就可以不用new
      A.B.hello();
      
      • 1
      • 2
      • 3
      • 4
      • 5
  • 相关阅读:
    KeyDB源码解析一——网络模型
    【英语:基础高阶_学术写作训练】J3.学术写作类别和写作形式
    【Linux】一同探访环境变量的奥秘
    LeetCode647:回文子串
    爬虫案例实战
    C语言:用函数删除字符串里面的空格
    计算机毕业设计ssm基于JAVA的食品类购物系统q645b系统+程序+源码+lw+远程部署
    Python-入门-列表(四)
    JSP SSH 个人银行储蓄系统myeclipse开发mysql数据库MVC模式java编程网页设计
    分类预测 | Matlab实现CNN-SVM多特征分类预测
  • 原文地址:https://blog.csdn.net/qq_43681877/article/details/126725716