六、面向对象编程(高级)

六.面向对象编程(高级)

1.类变量和类方法
  • 类变量就是类里边的静态变量/静态属性

    • 它是属于类的,而不是属于某个特定的对象。对于一个类中的不同对象来说,类变量只有一个且是可以共享的。(*举个例子,假设有一个学生类,类中有学生名字和此学生进去之后图书馆当前已经进的总人数(限制人数是100人),每个学生都是一个学生类的对象,这些学生对象进入的都是同一个图书馆,每来一个学生进入,类中的这个图书馆人数类变量就要++,从这个例子里体现类变量的作用*)
    //类变量声明,在类中
    //类变量可以在其他类中通过类名来访问,也可以使用对象实例访问
    class Student {
        private String name;
        //定义一个变量 count ,是一个类变量(静态变量) static 静态
        //该变量最大的特点就是会被 Student 类的所有的对象实例共享
        public static int count = 0;
    }
    
  • 类变量内存布局

    • 根据JDK,类变量可能会放在不同位置(堆空间、方法去静态域中),但是不影响所有对象都是共享同一个类变量,且在类加载中就生成了
  • 类变量定义和访问

    class A {
        public static String name = "njm"; //推荐访问修饰符放在前面
        static public String name = "njm"; 
    }
    class B {
        //通过类名调用类变量,不需要预先创建对象实例,但要注意访问权限
        System.out.println(A.name);//推荐用类名调用
        //通过对象调用类变量
        A a = new A();
        System.out.println(a.name);
    }
    
  • 类变量使用细节

    • 什么时候需要类变量:需要让所有对象都共享一个变量时
    • 类变量与实例变量(普通属性)的区别:实例变量是每个对象独享的,实例变量不能通过类名.实例变量来调用
    • 加上static称为类变量和静态变量,否则称为实例变量、普通变量、非静态变量、非静态成员变量
    • 类变量的声明周期是随类的加载开始,随着类的消亡而销毁
  • 类方法就是静态方法,静态方法可以访问静态属性,非静态方法也可以访问静态属性

  • 类方法使用细节

    • 什么时候需要类方法:当方法中不涉及到任何和对象相关的成员,则可以将方法设置成静态方法,提高开发效率(不创建实例也能调用方法,即把方法当成工具类使用时)
    • 类方法中不可以使用和对象有关的关键字,比如this、super
    • 类方法中只能用静态变量和静态方法普通方法既可以访问普通变量/方法,也可以访问静态变量/方法
2.理解main函数入口
public static void main(String[] args) { }
  • main方法时虚拟机来调用,所以必须是public才能运行!!
  • Java虚拟机在执行main()时不创建对象,所以main必须是static(使用类名直接调用)
  • main接受String类型的数组参数,该数组中保存执行Java命令时传递给所运行类的参数
    • 在命令行运行java时,后边直接跟参数(可变长)即可
  • 静态方法main要访问本类的非静态成员,需要先创建对象,再调用即可
  • main的动态传值:在IDEA设置一下,使每次点击运行时都自动传数组参数,具体设置自行百度吧
3.代码块
  • 代码块又叫初始化块,属于类中的成员,类似于方法,将逻辑封装在{}中,但和方法不同的是没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或者类显示调用,而是在加载类或者创建对象时隐式调用

    [修饰符]{
        代码
    };  
    //1.最后分号可有可无
    //2.修饰符要么不屑,要么是static
    //3.static修饰的角静态代码块,没有其修饰的是普通代码块/非静态代码块
    
  • 应用场景:如果多个构造器中都存在重复的语句,可以抽取到初始化块中,提高代码的重用性

  • 使用细节

    • 静态代码块的作用就是对类初始化,随着类加载而执行,且只执行一次;普通代码块,每创建一个新对象就执行

    • (⭐)类什么时候被加载

      • 创建对象实例时
      • 创建子类对象实例,父类也被加载,而且父类先被加载
      • 使用类的静态成员(静态属性、静态方法)
    • 普通代码块,在创建对象实例时,会被隐式的调用,创建一个对象就调用一次;如果只是使用类的静态成员时,普通代码块并不会执行

    • (⭐)创建一个对象时,类中的调用顺序

      1. 调用静态代码块静态属性初始化(这两个没有优先级,按照定义的顺序)
      2. 调用普通代码块普通属性初始化(这两个没有优先级,按照定义的顺序)
      3. 调用类的构造方法
    • 构造器的最前面隐含了super()和调用普通代码块,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的

    class A {
        public A() { //构造器
            //这里有隐藏的执行要求
            //(1)super()
            //(2)调用普通代码块
            // 构造器的代码
        }
    }
    
    • (⭐)创建一个子类对象时,类中的调用顺序
      1. 父类调用静态代码块静态属性初始化(这两个没有优先级,按照定义的顺序)
      2. 子类调用静态代码块静态属性初始化(这两个没有优先级,按照定义的顺序)
      3. 父类调用普通代码块普通属性初始化(这两个没有优先级,按照定义的顺序)
      4. 父类的构造方法
      5. 子类调用普通代码块普通属性初始化(这两个没有优先级,按照定义的顺序)
      6. 子类的构造方法
    • 静态代码块只能调用静态成员(静态属性和静态方法);普通代码块可以调用任意成员
4.单例设计模式
  • 设计模式:大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式

  • 单例设计模式:每个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法

    • 饿汉式:
    class GirlFriend 
        private String name;
        //public static  int n1 = 100;
        //为了能够在静态方法中,返回 gf对象,需要将其修饰为static
        //對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用.
        private static GirlFriend gf = new GirlFriend("小红红");
        //如何保障我们只能创建一个 GirlFriend 对象
        //步骤[单例模式-饿汉式]
        //1. 将构造器私有化
        //2. 在类的内部直接创建对象(该对象是static)
        //3. 提供一个公共的static方法,返回 gf对象
        private GirlFriend(String name) {
            System.out.println("構造器被調用.");
            this.name = name;
        }
        public static GirlFriend getInstance() {
            return gf;
        }
    }
    
    • 懒汉式
    class Cat {
        private String name;
        public static  int n1 = 999;
        private static Cat cat ; //默認是null
        //步驟
        //1.仍然構造器私有化
        //2.定義一個static靜態屬性對象
        //3.提供一個public的static方法,可以返回一個Cat對象
        //4.懶漢式,只有當用戶使用getInstance時,才返回cat對象, 後面再次調用時,會返回上次創建的cat對象
        //  從而保證了單例
        private Cat(String name) {
            System.out.println("構造器調用...");
            this.name = name;
        }
        public static Cat getInstance() {
    
            if(cat == null) {//如果還沒有創建cat對象
                cat = new Cat("小可愛");
            }
            return cat;
        }
    }
    
  • 饿汉式VS懒汉式

    • 创建对象时机不同,饿汉式是类加载就创建,懒汉式是在使用时才创建
    • 饿汉式不存在线程安全问题,懒汉式存在线程安全问题
5.final关键字
  • 基本介绍

    • 不希望类被继承时,可以用final修饰

    • 不希望父类的某个方法被子类覆盖/重写时,可以用final

      public final void f(){}
      
    • 不希望类的某个属性值被修改时,可以用final

    • 不希望方法中的某个局部变量值被修改时,可以用final

  • 使用细节

    • final修饰的属性又叫常量,一半用XX_XX_XX命名
    • final修饰的属性必须在定义时赋初值,并且不能再修改,赋值可以在以下位置之一(注意只能选一个)
      • 定义时直接赋值
      • 构造器中
      • 代码块中
    • 如果final修饰的属性是静态的,则舒适化的位置只能时以下位置之一(注意只能选一个)
      • 定义时直接赋值
      • 静态代码块中
    • final类不能继承,但可以实例化
    • 类不是final,但是含有final方法,该方法虽然不能重写,但是可以被继承
    • 如果一个类是final类,没必要再将里边的方法修饰成final
    • final不能修饰构造器
      • 由于构造方法的独特性质,它已经具备了某种意义上的“最终性”,即每个类自己有自己的构造器,且自己的多个构造器之间不存在覆盖,所以谈不上继承、重写的事儿
    • final和static搭配使用(谁先谁后都可以),效率更高
      • 因为底层编译器做了优化处理,调用属性不会导致类加载
    • 包装类和String都是final类
6.抽象类
  • 当父类的某些方法需要声明,但是又不确定如何实现时,可以把它声明为抽象方法,那么这个类就是抽象类
abstract class A {  //抽象类
    private String name;
	public A (String name) {
		this.name = name;
	}
    public abstract void eat() ;  //抽象方法,没有方法体
}
  • 一个抽象方法必须在抽象类中,抽象类中不一定有抽象方法,抽象类中还可以有普通方法
  • 抽象类的价值在于设计,设计好之后让子类继承并实现。是考官喜欢提问的知识点,在框架和设计模式中使用较多
  • 抽象类使用细节
    • 抽象类不能被实例化,但可以有任意成员(非抽象方法、构造器、静态属性等等)
    • abstract只能修饰类和方法,不能修饰属性和其它的
    • 抽象方法不能有主体(即不能有{})
    • 一个类继承了抽象类,则它必须实现抽象类的所有抽象方法
    • 抽象类不能使用private、final、static来修饰,因为这些关键字都是和重写相违背的
      • private私有的不能被子类重写
      • final根本不能被继承,更别提重写
      • static关键字与方法重写无关
7.抽象类的实践-模版设计模式
  • 模版设计模式是23种基本模式之一,利用到了抽象类,抽象类作为多个子类的通用模版,子类在抽象类的基础上进行扩展、改造,但子类总体上回保留抽象类的行为方式

  • 模版设计模式能解决的问题

    • 当内部功能的一部分是确定的,一部分是不确定的,就可以把不确定的部分暴露出去,由子类去实现
  • 实践

    • 要求:(1)有多个类,分别完成各自不同的任务 (2)要求统计得到各自任务的完成时间
    • 思考:要求1是根据每个任务的不同,需要编写不同的处理代码,而要求2在各自任务中都是记录任务开始和任务结束时间并做减法,所以把执行任务写成抽象方法暴露给各子类,而将记录时间写在父类(模板类)中
    • 代码:
    package com.hspedu.abstract_;
    abstract public class Template { //抽象类-模板设计模式
    	public abstract void job();//抽象方法
    	public void calculateTime() {//实现方法,调用 job 方法
    		//得到开始的时间
    		long start = System.currentTimeMillis();
        	job(); //动态绑定机制
    		//得的结束的时间
    		long end = System.currentTimeMillis();
    		System.out.println("任务执行时间 " + (end - start));
    	}
    }
    
    package com.hspedu.abstract_;
    public class AA extends Template {
        //计算任务
        //1+....+ 800000
        @Override
        public void job() { //实现 Template 的抽象方法 job
            long num = 0;
            for (long i = 1; i <= 800000; i++) {
            	num += i;
            }
        }
    }
    
    package com.hspedu.abstract_;
    public class BB extends Template{
        public void job() {//这里也去,重写了 Template 的 job 方法
            long num = 0;
            for (long i = 1; i <= 80000; i++) {
                num *= i;
            }
        }
    }
    
    package com.hspedu.abstract_;
    public class TestTemplate {
    	public static void main(String[] args) {
        	AA aa = new AA();
    		aa.calculateTime(); //这里还是需要有良好的 OOP 基础,对多态
    		BB bb = new BB();
    		bb.calculateTime();
    	}
    }
    
    • 上述代码中注意程序的运行顺序:aa.calculateTime() -> Template.calculateTime() -> aa.job
      • 从子类AA中找不到calculateTime()方法,就向上查找,找到了父类的calculateTime()方法并执行,执行过程中调用了job()方法,注意有动态绑定机制,所以调用方法一定是先从运行类型开始找,即父类的emplate.calculateTime()执行子类AA的aa.job方法
8.接口
  • 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来

    interface 接口名{
        //属性
        //抽象方法
    }
    class 类名 implements 接口{
        //自己属性
        //自己方法
        //必须实现的接口的抽象方法
    }
    
  • 接口的使用细节

    • 接口不能被实例化
    • 接口中所有方法都是public方法,接口中抽象方法可以不用abstract修饰
    void aaa();
    //实际上是abstract void aaa();
    
    • 一个普通类实现接口,必须将该接口的所有方法实现;抽象类实现接口,可以不用实现接口的方法

      • 当接口中方法过多时,可将光标放在接口上按alt+enter,可一次性将所有接口中方法实现
    • 一个类同时可以实现多个接口

    • 接口中属性是public static final的

    int a = 1;
    // 实际上是 public static final int a = 1;  //必须初始化
    
    • 接口中属性的访问形式:接口名.属性名
    • 接口不能继承类,但可以继承多个别的接口
    • 接口的修饰符只能是public和默认,这点和类的修饰符是一样的
  • 练习

    interface A {
        int a = 23;
    }
    class B implements A {
    }
    main中:
    B b = new B();  //ok
    System.out.println(b.a); //23
    System.out.println(A.a);  //23
    System.out.println(B.a);  //23
    
  • 接口和抽象类的对比

    参数抽象类接口
    方法定义抽象类中既可以包含抽象方法,也可以包含非抽象方法(即有具体实现的方法)接口中的所有方法默认都是抽象的(在Java 8之前),即只有方法签名而无方法体,Java 8之后,接口还可以包含静态方法(static methods)和默认方法(default methods),这两者都可以有方法体的实现
    方法实现子类使用extends来继承,子类如果不是抽象类,必须实现抽象类中所有抽象方法子类通过implements实现接口,需要提供接口中所有方法的实现
    构造器可以有不能有
    继承单继承可以多继承
    成员变量及方法有多种访问修饰符 private、protected或者public变量默认是public static final的,即接口常量;方法默认是public
    速度比接口速度快接口稍微慢,因为需要时间去寻找在类中的实现方法
    解决问题继承类解决代码的复用性和可维护性接口的截止主要在于设计,设计好各种规范,让其他类实现
  • 一个类继承父类和实现接口可以理解为本能和技能

  • 接口的多态特性

    • 多态参数

      • 当方法定义时参数是一个接口时,实际传进去的参数可以是任何实现了此接口的类实例对象
    • 多态数组

      package com.hspedu.interface_;
      
      public class InterfacePolyArr {
          public static void main(String[] args) {
              //多态数组 -> 接口类型数组
              Usb[] usbs = new Usb[2];
              usbs[0] = new Phone_();
              usbs[1] = new Camera_();
              /*
              给Usb数组中,存放 Phone  和  相机对象,Phone类还有一个特有的方法call(),遍历Usb数组,如果是Phone对象,除了调用Usb 接口定义的方法外,还需要调用Phone 特有方法 call
               */
              for(int i = 0; i < usbs.length; i++) {
                  usbs[i].work();//动态绑定..
                  //和前面一样,我们仍然需要进行类型的向下转型
                  if(usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_
                      ((Phone_) usbs[i]).call();
                  }
              }
          }
      }
      
      interface Usb{
          void work();
      }
      class Phone_ implements Usb {
          public void call() {
              System.out.println("手机可以打电话...");
          }
          @Override
          public void work() {
              System.out.println("手机工作中...");
          }
      }
      class Camera_ implements Usb {
          @Override
          public void work() {
              System.out.println("相机工作中...");
          }
      }
      
    • 接口多态传递现象

    package com.hspedu.interface_;
    /**
     * 演示多态传递现象
     */
    public class InterfacePolyPass {
        public static void main(String[] args) {
            //接口类型的变量可以指向,实现了该接口的类的对象实例
            IG ig = new Teacher();
            //如果IG 继承了 IH 接口,而Teacher 类实现了 IG接口
            //那么,实际上就相当于 Teacher 类也实现了 IH接口.
            //这就是所谓的 接口多态传递现象.
            IH ih = new Teacher();
        }
    }
    
    interface IH {
        void hi();
    }
    interface IG extends IH{ }
    class Teacher implements IG {
        @Override
        public void hi() {
        }
    }
    
9.内部类(⭐重难点!!!)
  • 类的五大成员:(1)变量/属性(2)方法(3)构造器(4)代码块(5)内部类

  • 一个类的内部又完整的嵌套了另一个类,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class),内部类最大特点就是可以直接访问私有属性,并且体现类之间的包含关系

    class Outer { //外部类
        class Inner {  //内部类       
        }
    }
    
  • 内部类的分类

    • 定义在外部类局部位置上(比如方法内):
      • 局部内部类(有类名)
      • 匿名内部类(没有类名,⭐重点
    • 定义在外部类的成员位置上:
      • 成员内部类(没有用static修饰)
      • 静态内部类(使用static修饰)
  • 局部内部类

    • 定义:定义在外部类的局部位置,比如方法、代码块中
    • 可以直接访问外部类的所有成员,包含私有的,因为本质上就是在这个类中使用的private成员
    • 不能添加访问修饰符,因为他是一个局部变量,局部变量不需要被外界调用,只需要在局部位置(方法或者代码块)中使用,但可以给局部内部类添加final修饰,将不能被修改
    • 作用域:定义它的代码块或者方法中
    • 局部内部类访问外部类成员----->直接访问
    • 外部类访问局部内部类成员----->创建对象再访问(必须在作用域中)
    • 外部其他类不能访问局部内部类(因为局部内部类本质是局部变量)
    • 如果外部类和局部内部类的成员重名,默认就近原则,如果局部内部类想访问外部类的成员,可以用(外部类.this.成员
    class Outer02 {//外部类
        private int n1 = 100;
        private void m2() {
            System.out.println("Outer02 m2()");
        }//私有方法
        public void m1() {//方法
            final class Inner02 {//局部内部类(本质仍然是一个类)
                private int n1 = 800;
                public void f1() {
                    //   老韩解读 Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer02.this就是哪个对象
                    System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);
                    System.out.println("Outer02.this hashcode=" + Outer02.this);
                    m2();
                }
            }
        }
    
    }
    
  • 匿名内部类(⭐)

    • 本质是一个继承父类方法的子类或者实现接口的类,而且是内部类,该类没有名字,使用父类或者接口来声明编译类型即可使用(例如下边的tiger对象,编译类型是接口,运行类型是匿名内部类)
    • 基于接口的匿名内部类
    class Outer04 { //外部类
        private int n1 = 10;//属性
        public void method() {//方法
            //基于接口的匿名内部类
            //老韩解读
            //1.需求: 想使用IA接口,并创建对象
            //2.传统方式,是写一个类,实现该接口,并创建对象
            //3.老韩需求是 Tiger/Dog 类只是使用一次,后面再不使用
            //4. 可以使用匿名内部类来简化开发
            //5. tiger的编译类型 ? IA
            //6. tiger的运行类型 ? 就是匿名内部类  Outer04$1
            /*
                我们看底层 会分配 类名 Outer04$1
                class Outer04$1 implements IA {
                    @Override
                    public void cry() {
                        System.out.println("老虎叫唤...");
                    }
                }
             */
            //7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址
            //   返回给 tiger
            //8. 匿名内部类使用一次,就不能再使用
            //tiger编译类型:IA   运行类型:匿名内部
            IA tiger = new IA() {
                @Override
                public void cry() {
                    System.out.println("老虎叫唤...");
                }
            };
            System.out.println("tiger的运行类型=" + tiger.getClass());
            tiger.cry();
            tiger.cry();
            tiger.cry();//匿名内部类不能多次使用,但实例可以多次使用
    }
    
    interface IA {//接口
        public void cry();
    }
    
    • 基于类的匿名内部类
    class Outer04 { //外部类
        private int n1 = 10;//属性
        public void method() {//方法
            //演示基于类的匿名内部类
            //分析
            //1. father编译类型 Father
            //2. father运行类型 Outer04$2
            //3. 底层会创建匿名内部类
            /*
                class Outer04$2 extends Father{
                    @Override
                    public void test() {
                        System.out.println("匿名内部类重写了test方法");
                    }
                }
             */
            //4. 同时也直接返回了 匿名内部类 Outer04$2的对象
            //5. 注意("jack") 参数列表会传递给 构造器
            Father father = new Father("jack"){
                @Override
                public void test() {
                    System.out.println("匿名内部类重写了test方法");//只要重写了就不再是本类,而是继承类,在这里是匿名内部类
                }
            };
            System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2
            father.test();
        }
    }
    
    class Father {//类
        public Father(String name) {//构造器
            System.out.println("接收到name=" + name);
        }
        public void test() {//方法
        }
    }
    
    • 基于抽象类的匿名内部类
    class Outer04 { //外部类
        private int n1 = 10;//属性
        public void method() {//方法
            //基于抽象类的匿名内部类
            Animal animal = new Animal(){
                @Override
                void eat() {
                    System.out.println("小狗吃骨头...");
                }
            };
            animal.eat();
        }
    }
    
    abstract class Animal { //抽象类
        abstract void eat();
    }
    
    • 匿名内部类的细节

      • 两种使用方法
      class Outer05 {
          private int n1 = 99;
          public void f1() {
              //1.可以用父类接收子类对象
              Person p = new Person(){
                  private int n1 = 88;
                  @Override
                  public void hi() {
                      System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +
                              " 外部内的n1=" + Outer05.this.n1 );
                      //Outer05.this 就是调用 f1的 对象,这里就是Outer05类的对象
                      System.out.println("Outer05.this hashcode=" + Outer05.this);
                  }
              };
              p.hi();//动态绑定, 运行类型是 Outer05$1
      
              //2.也可以直接调用, 匿名内部类本身也是返回对象
      //         class 匿名内部类 extends Person {}
              new Person(){
                  @Override
                  public void hi() {
                      System.out.println("匿名内部类重写了 hi方法,哈哈...");
                  }
              }.hi;
          }
      }
      
      class Person {//类
          public void hi() {
              System.out.println("Person hi()");
          }
      }
      
      • 匿名内部类语法奇特,因为它既是一个类的定义,同时它本身也是一个对象。从语法角度看,既有定义类的特征,也有创建对象的特征。
      • 可以直接访问外部类的所有成员,包含私有的
      • 不能添加访问修饰符,因为本质是局部变量(局部变量没必要加访问修饰符)
      • 作用域:方法或者代码块中
      • 外部其他类不能访问匿名内部类(因为匿名内部类本质是局部变量)
      • 如果外部类和匿名内部类的成员重名,默认就近原则,如果匿名内部类里想访问外部类的成员,可以用(外部类.this.成员
    • 匿名内部类的最佳实践

      • 当作实参直接传递,简洁高效
      public void f1( new IL(){  //IL是个接口
          @override
          public void shouw() {
              System.out.println("这是匿名内部类作为实参");
          }
      }) {
          //f1函数的方法体
      }
      
  • 成员内部类

    • 定义在外部类中的成员位置上,可以直接访问外部类的所有成员,包括私有的

    • 可以添加任意访问修饰符,因为它就是一个成员

    • 作用域:和外部类的其他成员一样,作用域为整个类

    • 成员内部类访问外部类成员------>直接访问

    • 外部类访问成员内部类-------->创建成员内部类对象,再访问

    • 外部其他类访问成员内部类------->两种方式

      • 先创建外部类对象,再用外部类对象创建内部类对象
      // 第一种方式
      // outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员
      // 这就是一个语法,不要特别的纠结.
       Outer08 outer08 = new Outer08();
      Outer08.Inner08 inner08 = outer08.new Inner08();
      
      • 外部类中,编写方法,在外部其他类中创建外部类对象,然后调用此方法
      // 第二方式 在外部其他类中创建外部类对象
      Outer08 outer08 = new Outer08();//外部类对象
      Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
      
      //外部类中的调用方法
       public Inner08 getInner08Instance(){
              return new Inner08();
      }
      
    • 如果外部类和成员内部类的成员重名,默认就近原则,如果成员内部类里想访问外部类的成员,可以用(外部类.this.成员

  • 静态内部类

    • 定义在外部类中的成员位置上,可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员

    • 可以添加任意访问修饰符,因为它就是一个成员

    • 作用域:和外部类的其他成员一样,作用域为整个类

    • 静态内部类访问外部类成员------>直接访问所有静态成员(只能访问静态成员

    • 外部类访问静态内部类-------->创建静态内部类对象,再访问

    • 外部其他类访问静态内部类------->两种方式

      //方式1
      //因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
      Outer10.Inner10 inner10 = new Outer10.Inner10();
      inner10.say();
      
      //方式2
      //编写一个方法,可以返回静态内部类的对象实例.
      Outer10 outer10 = new Outer10();//外部类对象
      Outer10.Inner10 inner101 = outer10.getInner10();
      inner101.say();
      //getInner10()定义
      public Inner10 getInner10() {
          return new Inner10();
      }
      
      //方式2的简化
      //不创建外部类对象直接调用方法,将方法设成static
      Outer10.Inner10 inner10_ = Outer10.getInner10_();
      System.out.println("************");
      inner10_.say();
      //Inner10 getInner10_() 定义
      public static Inner10 getInner10_() {
          return new Inner10();
      }
      
    • 如果外部类和静态内部类的成员重名,默认就近原则,如果静态内部类里想访问外部类的成员,可以用(外部类.成员

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值