乐趣区

关于后端:Java面向对象编程高级

类变量和类办法

类变量 - 提出问题

在 main 办法中定义一个变量 count,当一个小孩退出游戏后 count++,最初个 count 就记录有多少小孩玩游戏。

问题剖析:

count 是一个独立于对象, 很难堪,当前咱们拜访 count 很麻烦, 没有应用到 OOP。因而, 咱们引出类变量 / 动态变量。

package com.hspedu.static_;

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

        // 定义一个变量 count, 统计有多少小孩退出了游戏
        int count = 0;

        Child child1 = new Child("白骨精");
        child1.join();
        //count++;
        child1.count++;

        Child child2 = new Child("狐狸精");
        child2.join();
        //count++;
        child2.count++;

        Child child3 = new Child("老鼠精");
        child3.join();
        //count++;
        child3.count++;

        //===========
        // 类变量,能够通过类名来拜访
        System.out.println("共有" + Child.count  + "小孩退出了游戏...");
        // 上面的代码输入什么?
        System.out.println("child1.count=" + child1.count);//3
        System.out.println("child2.count=" + child2.count);//3
        System.out.println("child3.count=" + child3.count);//3



    }
}

class Child { // 类
    private String name;
    // 定义一个变量 count , 是一个类变量(动态变量) static 动态!!!
    // 该变量最大的特点就是会被 Child 类的所有的对象实例共享!!!
    public static int count = 0;
    public Child(String name) {this.name = name;}
    public void join() {System.out.println(name + "退出了游戏..");
    }
}

类变量内存布局

https://blog.csdn.net/x_iya/article/details/81260154/

https://www.zhihu.com/question/59174759/answer/163207831

有些书说在办法区 … jdk 版本有关系, 记住两点:

(1) static 变量是同一个类所有对象共享

(2) static 类变量,在类加载的时候就生成了. 动态变量是类加载的时候,就创立了, 所以不必创建对象实例也能间接通过 类名. 类变量名 拜访。

什么是类变量

类变量也叫动态变量 / 动态属性,是该类的所有对象共享的变量, 任何一个该类的对象去拜访它时, 取到的都是雷同的值, 同样任何一个该类的对象去批改它时, 批改的也是同一个变量。这个从后面的图也可看进去。

如何定义类变量

定义语法:

拜访修饰符 static 数据类型变量名;[举荐]

static 拜访修饰符数据类型变量名;

如何拜访类变量

类名. 类变量名

或者对象名. 类变量名【动态变量的拜访修饰符的拜访权限和范畴和一般属性是一样的】

举荐应用: 类名. 类变量名;

类变量应用注意事项

1. 什么时候须要用类变量

当咱们须要让某个类的所有对象都共享一个变量时,就能够思考应用类变量(动态变量): 比方: 定义学生类,统计所有学生共交多少钱。Student (name, staticfee)

2. 类变量与实例变量 (一般属性) 区别

类变量是该类的所有对象共享的, 而实例变量是每个对象独享的。

3. 加上 static 称为类变量或动态变量,否则称为实例变量 / 一般变量 / 非动态变量

4. 类变量能够通过类名. 类变量名或者对象名. 类变量名来拜访,但 java 设计者举荐咱们应用类名. 类变量名形式拜访。【前提是满足拜访修饰符的拜访权限和范畴】

5. 实例变量不能通过类名. 类变量名形式拜访。

6. 类变量是在类加载时就初始化了,也就是说,即便你没有创建对象,只有类加载了. 就能够应用类变量了。

7. 类变量的生命周期是随类的加载开始, 随着类沦亡而销毁。

类办法根本介绍

类办法也叫静态方法。模式如下:

拜访修饰符 static 数据返回类型 办法名(){}【举荐】

static 拜访修饰符 数据返回类型 办法名(){}

类办法的调用

应用形式:

类名. 类办法名或者对象名. 类办法名

package com.hspedu.static_;

public class StaticMethod {public static void main(String[] args) {
        // 创立 2 个学生对象,叫学费
        Stu tom = new Stu("tom");
        //tom.payFee(100);
        Stu.payFee(100);// 对不对? 对

        Stu mary = new Stu("mary");
        //mary.payFee(200);
        Stu.payFee(200);// 对


        // 输入以后收到的总学费
        Stu.showFee();//300

        // 如果咱们心愿不创立实例,也能够调用某个办法(即当做工具来应用)
        // 这时,把办法做成静态方法时十分适合
        System.out.println("9 开平方的后果是 =" + Math.sqrt(9));


        System.out.println(MyTools.calSum(10, 30));
    }
}
// 开发本人的工具类时,能够将办法做成动态的,不便调用
class MyTools  {
    // 求出两个数的和
    public static double calSum(double n1, double n2) {return  n1 + n2;}
    // 能够写出很多这样的工具办法...
}
class Stu {
    private String name;// 一般成员
    // 定义一个动态变量,来累积学生的学费
    private static double fee = 0;

    public Stu(String name) {this.name = name;}
    // 阐明
    // 1. 当办法应用了 static 润饰后,该办法就是静态方法
    // 2. 静态方法就能够拜访动态属性 / 变量
    public static void payFee(double fee) {Stu.fee += fee;// 累积到}
    public static void showFee() {System.out.println("总学费有:" + Stu.fee);
    }
}

类办法经典的应用场景

当办法中不波及到任何和对象相干的成员,则能够将办法设计成静态方法, 进步开发效率。

比方:

工具类中的办法 utils。Math 类、Arrays 类、Collections 汇合类看下源码能够发现都是 static 办法。

类办法应用注意事项和细节探讨

  1. 类办法和一般办法都是随着类的加载而加载,将构造信息存储在办法区∶类办法中无 this 的参数。一般办法中隐含着 this 的参数。
  2. 类办法能够通过类名调用,也能够通过对象名调用。一般办法和对象无关,须要通过对象名调用,比方对象名. 办法名(参数),不能通过类名调用。
  3. 类办法中不容许应用和对象无关的关键字 ,比方 this 和 super。一般办法(成员办法) 能够。
  4. 类办法 (静态方法) 中只能拜访动态变量或静态方法。一般成员办法, 既能够拜访非动态成员, 也能够拜访动态成员!!
package com.hspedu.static_;

public class StaticMethodDetail {public static void main(String[] args) {D.hi();//ok
        // 非静态方法,不能通过类名调用
        //D.say();, 谬误,须要先创建对象,再调用
        new D().say();// 能够
    }
}
class D {

    private int n1 = 100;
    private static  int n2 = 200;
    public void say() {// 非静态方法, 一般办法}

    public static  void hi() {// 静态方法, 类办法
        // 类办法中不容许应用和对象无关的关键字,// 比方 this 和 super。一般办法 (成员办法) 能够。//System.out.println(this.n1);
    }

    // 类办法 (静态方法) 中 只能拜访 动态变量 或静态方法
    // 口诀: 静态方法只能拜访动态成员.
    public static void hello() {System.out.println(n2);
        System.out.println(D.n2);
        //System.out.println(this.n2); 不能应用
        hi();//OK
        //say();// 谬误}
    // 一般成员办法,既能够拜访  非动态成员,也能够拜访动态成员
    // 小结: 非静态方法能够拜访 动态成员和非动态成员
    public void ok() {
        // 非动态成员
        System.out.println(n1);
        say();
        // 动态成员
        System.out.println(n2);
        hello();}
}

练习:

package com.hspedu.static_;

public class StaticExercise03 {
}

class Person {
    private int id;
    private static int total = 0;
    public static void setTotalPerson(int total){
        // this.total = total;// 谬误,因为在 static 办法中,不能够应用 this 关键字

        Person.total = total;
    }
    public Person() {// 结构器
        total++;
        id = total;
    }
    // 编写一个办法,输入 total 的值
    public static void m() {System.out.println("total 的值 =" + total);
    }
}
class TestPerson {public static void main(String[] args) {Person.setTotalPerson(3); // 这里没有调用结构器
        new Person(); // new 了之后调用结构器,count++
        Person.m();// 最初 total 的值就是 4}
}

留神:

Person.setTotalPerson(3); 调用静态方法 这里还没有调用结构器

new Person(); new 了之后才调用结构器,count++

因为结构器是在创建对象时实现对对象的初始化。

了解 main 办法语法

深刻了解 main 办法

解释 main 办法的模式: public static void main(String[] args){}

1.main 办法时虚拟机调用

2.java 虚拟机须要调用类的 main()办法,所以该办法的拜访权限必须是 public

3.java 虚拟机在执行 main()办法时不用创建对象,所以该办法必须是 static

4. 该办法接管 String 类型的数组参数,该数组中保留执行 java 命令时传递给所运行的类的参数, 案例演示,接管参数.

5.java 执行的程序参数 1 参数 2 参数。

阐明: 在 idea 如何传递参数?

在 Program arguments 中传入参数即可。

特地提醒

在 main()办法中,咱们能够间接调用 main 办法所在类的静态方法或动态属性。然而,不能间接拜访该类中的非动态成员,必须创立该类的一个实例对象后,能力通过这个对象去拜访类中的非动态成员。

代码块

根本介绍

代码化块又称为初始化块, 属于类中的成员[即是类的一部分],相似于办法,将逻辑语句封装在办法体中,通过包围起来。

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

根本语法

[修饰符]{代码};

阐明留神;

  1. 修饰符可选, 要写的话, 也只能写 static
  2. 代码块分为两类,应用 static 润饰的叫动态代码块,没有 static 润饰的,叫一般代码块 / 非动态代码块。
  3. 逻辑语句能够为任何逻辑语句(输出、输入、办法调用、循环、判断等)
  4. ;号能够写上, 也能够省略。

代码块的益处和案例演示

  1. 相当于另外一种模式的结构器(对结构器的补充机制),能够做初始化的操作
  2. 场景: 如果多个结构器中都有反复的语句,能够抽取到初始化块中,进步代码的重用性

这样当咱们不论调用哪个结构器,创建对象,都会先调用代码块的内容,代码块调用的程序优先于结构器。

package com.hspedu.codeblock_;

public class CodeBlock01 {public static void main(String[] args) {Movie movie = new Movie("你好,李焕英");
        System.out.println("===============");
        Movie movie2 = new Movie("唐探 3", 100, "陈思诚");
    }
}

class Movie {
    private String name;
    private double price;
    private String director;

    // 3 个结构器 -》重载
    {System.out.println("电影屏幕关上...");
        System.out.println("广告开始...");
        System.out.println("电影正是开始...");
    };

    public Movie(String name) {System.out.println("Movie(String name) 被调用...");
        this.name = name;
    }

    public Movie(String name, double price) {

        this.name = name;
        this.price = price;
    }

    public Movie(String name, double price, String director) {System.out.println("Movie(String name, double price, String director) 被调用...");
        this.name = name;
        this.price = price;
        this.director = director;
    }
}

代码块应用注意事项和细节探讨!!!

  1. static 代码块也叫动态代码块,作用就是对类进行初始化,而且它随着 类的加载 而执行,并且只会执行一次。如果是一般代码块,每创立一个对象, 就执行一次。
  2. 类什么时候被加载
  • 创建对象实例时(new)
  • 创立子类对象实例, 父类也会被加载
  • 应用类的动态成员时(动态属性, 静态方法)
  1. 一般的代码块,在创建对象实例 时,会被隐式的调用。被创立一次,就会调用一次。

如果只是应用类的动态成员时, 一般代码块并不会执行。(没有创建对象实例)

  1. 创立一个对象时,在一个类调用程序是 (重点,难点)
  2. 调用动态代码块和动态属性初始化 (留神: 动态代码块和动态属性初始化调用的优先级一样,如果有多个动态代码块和多个动态变量初始化,则按他们定义的先后顺序调用)
  3. 调用一般代码块和一般属性的初始化(留神: 一般代码块和一般属性初始化调用的优先级一样, 如果有多个一般代码块和多个一般属性初始化, 则按定义先后顺序调用)
  4. 调用构造方法
  5. 结构器的最后面其实隐含了 super() 和调用一般代码块, 动态相干的代码块,属性初始化,在类加载时,就执行结束,因而是优先于结构器和一般代码块执行的。
  6. 咱们看一下创立一个子类对象时(继承关系),他们的调用程序如下:
  7. 父类的动态代码块和动态属性(优先级一样, 按定义程序执行)(类加载)
  8. 子类的动态代码块和动态属性(优先级一样, 按定义程序执行)(类加载)
  9. 父类的一般代码块和一般属性初始化(优先级一样,按定义程序执行)
  10. 父类的构造方法
  11. 子类的一般代码块和一般属性初始化(优先级一样,按定义程序执行)
  12. 子类的构造方法
  13. 动态代码块(实质上是静态方法)只能间接调用动态成员(动态属性和静态方法),一般代码块(实质上是一般办法)能够调用任意成员。
package com.hspedu.codeblock_;

public class CodeBlockDetail04 {public static void main(String[] args) {
        // 老师阐明
        //(1) 进行类的加载
        //1.1 先加载 父类 A02 1.2 再加载 B02
        //(2) 创建对象
        //2.1 从子类的结构器开始
        //new B02();// 对象

        new C02();}
}

class A02 { // 父类
    private static int n1 = getVal01();
    static {System.out.println("A02 的一个动态代码块..");//(2)
    }
    {System.out.println("A02 的第一个一般代码块..");//(5)
    }
    pulic int n3 = getVal02();// 一般属性的初始化
    public static int getVal01() {System.out.println("getVal01");//(1)
        return 10;
    }

    public int getVal02() {System.out.println("getVal02");//(6)
        return 10;
    }

    public A02() {// 结构器
        // 暗藏
        //super()
        // 一般代码和一般属性的初始化......
        System.out.println("A02 的结构器");//(7)
    }

}

class C02 {
    private int n1 = 100;
    private static  int n2 = 200;

    private void m1() {}
    private static void m2() {}

    static {
        // 动态代码块,只能调用动态成员
        //System.out.println(n1); 谬误
        System.out.println(n2);//ok
        //m1();// 谬误
        m2();}
    {
        // 一般代码块,能够应用任意成员
        System.out.println(n1);
        System.out.println(n2);//ok
        m1();
        m2();}
}

class B02 extends A02 { //

    private static int n3 = getVal03();

    static {System.out.println("B02 的一个动态代码块..");//(4)
    }
    public int n5 = getVal04();
    {System.out.println("B02 的第一个一般代码块..");//(9)
    }

    public static int getVal03() {System.out.println("getVal03");//(3)
        return 10;
    }

    public int getVal04() {System.out.println("getVal04");//(8)
        return 10;
    }
    // 肯定要缓缓的去品..
    public B02() {// 结构器
        // 暗藏了
        //super()
        // 一般代码块和一般属性的初始化...
        System.out.println("B02 的结构器");//(10)
        // TODO Auto-generated constructor stub
    }
}

练习:

package com.hspedu.codeblock_;

public class CodeBlockExercise02 {
}

class Sample
{Sample(String s)
    {System.out.println(s);
    }
    Sample()
    {System.out.println("Sample 默认构造函数被调用");
    }
}
class Test{Sample sam1=new Sample("sam1 成员初始化");//
    static Sample sam=new Sample("动态成员 sam 初始化");//
    static{System.out.println("static 块执行");//
        if(sam==null)System.out.println("sam is null");
    }
    Test()// 结构器
    {System.out.println("Test 默认构造函数被调用");//
    }
    // 主办法
    public static void  main(String  str[])
    {Test a=new Test();// 无参结构器
    }

}

1. 动态成员 sam 初始化
2. static 块执行
3. sam1 成员初始化
4. Test 默认构造函数被调用

单例设计模式

什么是设计模式

静态方法和属性的经典应用

设计模式是在大量的实际中总结和理论化之后优选的代码构造、编程格调、以及解决问题的思考形式。设计模式就像是经典的棋谱, 不同的棋局, 咱们用不同的棋谱,免去咱们本人再思考和摸索。

什么是单例模式

  1. 所谓类的单例设计模式,就是采取肯定的办法保障在整个的软件系统中,对 某个类只能存在一个对象实例,并且该类只提供一个获得其对象实例的办法。
  2. 单例模式有两种形式:1) 饿汉式 2) 懒汉式

饿汉式

步骤如下:

  1. 结构器私有化 =》避免间接 new
  2. 类的外部创建对象
  3. 向外裸露一个动态的公共办法。getlnstance

饿汉式:有可能还没有用到这个对象,然而因为类的机制曾经将对象创立好了。在线程还没呈现之前就曾经实例化了, 因而饿汉式线程肯定是平安的。

package com.hspedu.single_;

public class SingleTon01 {public static void main(String[] args) {//        GirlFriend xh = new GirlFriend("小红");
//        GirlFriend xb = new GirlFriend("小白");

        // 通过办法能够获取对象
        GirlFriend instance = GirlFriend.getInstance();
        System.out.println(instance);
        // 都是同一个对象
        GirlFriend instance2 = GirlFriend.getInstance();
        System.out.println(instance2);

        System.out.println(instance == instance2);// T 同一个对象
        //System.out.println(GirlFriend.n1);
    }
}

// 有一个类,GirlFriend
// 只能有一个女朋友
class GirlFriend {

    private String name;
    // public static  int n1 = 100;
    // 为了可能在静态方法中,返回 gf 对象,须要将其润饰为 static
    // 對象,通常是分量級的對象, 餓漢式可能造成創建了對象,然而沒有应用.
    // 只有类加载了,就肯定创立了 gf 对象
    private static GirlFriend gf = new GirlFriend("小红红");

    // 如何保障咱们只能创立一个 GirlFriend 对象
    // 步骤[单例模式 - 饿汉式]
    // 1. 将结构器私有化
    // 2. 在类的外部间接创建对象(该对象是 static)
    // 3. 提供一个公共的 static 办法,返回 gf 对象
    private GirlFriend(String name) {System.out.println("構造器被調用.");
        this.name = name;
    }

    // 用 static 的目标就是在不创建对象的前提下间接调用
    public static GirlFriend getInstance() {return gf;}

    @Override
    public String toString() {
        return "GirlFriend{" +
                "name='" + name + '\'' +
                '}';
    }
}

懒汉式

懶漢式,只有當用戶应用 getInstance 時,才返回 cat 對象, 後面再次調用時,會返回上次創建的 cat 對象。

懒汉式可能会存在线程平安的问题。

package com.hspedu.single_;

/**
 * 演示懶漢式的單例模式
 */
public class SingleTon02 {public static void main(String[] args) {//new Cat("大黃");
        //System.out.println(Cat.n1);
        Cat instance = Cat.getInstance();
        System.out.println(instance);


        // 再次調用 getInstance
        Cat instance2 = Cat.getInstance();
        System.out.println(instance2);

        System.out.println(instance == instance2);//T

    }
}


// 心愿在程序運行過程中,只能創建一個 Cat 對象
// 应用單例模式
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;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

比拟

  1. 二者最次要的区别在于创建对象的机会不同: 饿汉式是在类加载就创立了对象实例, 而懒汉式是在应用时才创立。
  2. 饿汉式不存在线程平安问题,懒汉式存在线程平安问题。(前面学习线程后,会欠缺一把)。
  3. 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有应用,那么饿汉式创立的对象就节约了,懒汉式是应用时才创立,就不存在这个问题。
  4. 在咱们 javaSE 规范类中,java.lang.Runtime 就是经典的单例模式.

final 关键字

根本介绍

final 中文意思: 最初的, 最终的.

final 能够润饰类、属性、办法和局部变量

在某些状况下, 程序员可能有以下需要,就会应用到 final:

  1. 当不心愿类被继承时, 能够用 final 润饰.
  2. 当不心愿父类的某个办法被子类笼罩 / 重写 (override) 时, 能够用 final 关键字润饰。【案例演示: 拜访修饰符 final 返回类型办法名】
  3. 当不心愿类的的某个属性的值被批改, 能够用 final 润饰.(例如: public final double TAX RATE=0.08)
  4. 当不心愿某个局部变量被批改,能够应用 final 润饰(例如:final double TAX RATE=0.08)

final 应用注意事项和细节探讨

  1. final 润饰的属性又叫常量, 个别用 XX_XX_XX(大写)来命名
  2. final 润饰的属性在定义时, 必须赋初值, 并且当前不能再批改,赋值能够在如下地位之一:

定义时: 如 public final double TAX_RATE=0.08;

在结构器中

在代码块中

class AA {
/*
1. 定义时:如 public final double TAX_RATE=0.08;
2. 在结构器中
3. 在代码块中
*/
public final double TAX_RATE = 0.08;//1. 定义时赋值
public final double TAX_RATE2 ;
public final double TAX_RATE3 ;
public AA() {// 结构器中赋值
    TAX_RATE2 = 1.1;
    }
    {// 在代码块赋值
        TAX_RATE3 = 8.8;
    }
}
  1. 如果 final 润饰的属性是动态的,则初始化的地位只能是

①定义时

②在动态代码块(不能在结构器中赋值。因为结构器是在对象创立的时候才会进行赋值)

  1. final 类不能继承, 然而能够实例化对象。(实例化没问题)
  2. 如果类不是 final 类,然而含有 final 办法,则该办法尽管不能重写,然而能够被继承。(子类用是没问题的,尽管不能重写)
  3. 一般来说,如果一个类曾经是 final 类了,就没有必要再将办法润饰成 final 办法。(因为类既然不能被继承,也就相应无奈被重写)。
  4. final 不能润饰构造方法(即结构器)。
  5. final 和 static 往往搭配应用,效率更高,因为 不会导致类加载,底层编译器做了优化解决。
  6. 包装类(Integer,Double,Float,Boolean 等都是 final),String 也是 final 类。

抽象类

引出

当父类的某些办法,须要申明,然而又不确定如何实现时,能够将其申明为形象办法, 那么这个类就是抽象类。

所谓形象办法就是没有实现的办法,所谓没有实现就是指,没有办法体。

当一个类中存在形象办法时,须要将该类申明为 abstract 类,一般来说,抽象类会被继承,由其子类来实现形象办法。

abstract class Animal {
    private String name;

    public Animal(String name) {this.name = name;}
    public abstract void eat()  ;}

抽象类的介绍

1)用 abstract 关键字来润饰一个类时, 这个类就叫抽象类拜访修饰符

2)用 abstract 关键字来润饰一个办法时, 这个办法就是形象办法

拜访修饰符 abstract 返回类型 办法名(参数列表);// 没有办法体

3)抽象类的价值更多作用是在于设计, 是设计者设计好后,让子类继承并实现抽象类。

4)抽象类是考官比拟爱问的知识点,在框架和设计模式应用较多。

抽象类应用的注意事项和细节探讨

1)抽象类不能被实例化

2)抽象类不肯定要蕴含 abstract 办法。也就是说, 抽象类能够没有 abstract 办法。

3)一旦类蕴含了 abstract 办法, 则这个类必须申明为 abstract。

4)abstract 只能润饰类和办法,不能润饰属性和其它的。

5)抽象类能够有任意成员【抽象类实质还是类】,比方: 非形象办法、结构器、动态属性等等。

6)形象办法不能有主体,即不能实现

7)如果一个类继承了抽象类,则它必须实现抽象类的所有形象办法,除非它本人也申明为 abstract 类。

8)形象办法不能应用 private、final 和 static 来润饰,因为这些关键字都是和重写相违反的。

抽象类最佳实际 - 模板设计模式

根本介绍

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的根底上进行扩大、革新,但子类总体上会保留抽象类的行为形式。

模板设计模式能解决的问题

1)当性能外部一部分实现是确定,一部分实现是不确定的。这时能够把不确定的局部裸露进来,让子类去实现。
2)编写一个形象父类,父类提供了多个子类的通用办法,并把一个或多个办法留给其子类实现,就是一种模板模式.

最佳实际

需要:

有多个类,实现不同的工作 job

要求统计失去各自实现工作的工夫

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));
    }
}

以上就是把不确定的局部裸露进来,让子类去实现。

接口

根本介绍

接口就是给出一些没有实现的办法,封装到一起,到某个类要应用的时候,在依据具体情况把这些办法写进去。语法:

interface 接口名{
    // 属性
    // 形象办法(接口中能够省略 abstract 关键字)(在 jdk8 后还能够有静态方法和默认办法)}

class 类名 implements 接口 {
    // 本人属性;
    // 本人办法;
    // 必须实现的接口的形象办法
}

小结:

接口是更加形象的类。抽象类里的办法能够有办法体,接口里的所有办法都没有办法体(jdk7.0)。接口体现了程序设计的多态和高内聚低偶合的设计思维。

特地阐明:Jdk8.0 后接口类能够有静态方法(static),默认办法(default),也就是说接口中能够有办法的具体实现入。

深刻探讨

说当初有一个项目经理(段玉), 治理三个程序员, 性能开发一个软件. 为了管制和管理软件, 项目经理能够定义一些接口,而后由程序员具体实现。

通过接口,不仅能够对立办法名,同时在调用时只须要依据接口辨认即可。

package com.hspedu.interface_;

public interface DBInterface { // 项目经理

    public void connect();// 连贯办法
    public void close();// 敞开连贯}
package com.hspedu.interface_;
// A 程序
public class MysqlDB implements DBInterface {
    @Override
    public void connect() {System.out.println("连贯 mysql");
    }

    @Override
    public void close() {System.out.println("敞开 mysql");
    }
}
package com.hspedu.interface_;

// B 程序员连贯 Oracle
public class OracleDB implements DBInterface{

    @Override
    public void connect() {System.out.println("连贯 oracle");
    }

    @Override
    public void close() {System.out.println("敞开 oracle");
    }
}
package com.hspedu.interface_;

public class Interface03 {public static void main(String[] args) {MysqlDB mysqlDB = new MysqlDB();
        t(mysqlDB);
        OracleDB oracleDB = new OracleDB();
        t(oracleDB);
    }

    public static void t(DBInterface db) {db.connect();
        db.close();}
}

注意事项和细节

  1. 接口不能被实例化(new)
  2. 接口中所有的办法是 public 办法,接口中形象办法,能够不必 abstract 修

饰。void aaa(); 实际上是 abstract void aa();(同理,不写 public 也是默认 public 办法,因而实现时该办法不写 public 会报错。)

  1. 一个一般类实现接口, 就必须将该接口的所有办法都实现。
  2. 抽象类实现接口,能够不必实现接口的办法。
  3. 一个类同时能够实现多个接口
class Timer implements IA, IB{}
  1. 接口中的属性,只能是 final 的,而且是 public static final 修饰符。比方:int a=1; 实际上是 public static final int a=1; (必须初始化)
  2. 接口中属性的拜访模式: 接口名. 属性名
  3. 接口不能继承其它的类, 然而能够继承多个别的接口。(接口无奈实现接口)
interface A extends B,C{}
  1. 接口的修饰符只能是 public 和默认,这点和类的修饰符是一样的。

实现接口 VS 继承类

当子类继承了父类,就主动的领有父类的性能,如果子类须要扩大性能,能够通过实现接口的形式扩大。能够了解 实现接口 是对 java 单继承机制的一种补充。

  1. 接口和继承解决的问题不同

继承的价值次要在于: 解决代码的复用性和可维护性。

  1. 接口的价值次要在于: 设计,设计好各种标准(办法),让其它类去实现这些办法。即更加的灵便

接口比继承更加灵便:继承是满足 is – a 的关系,而接口只需满足 like – a 的关系。

接口在肯定水平上实现代码解耦[即: 接口规范性 + 动静绑定机制]

接口的多态个性

  1. 多态参数

在后面的 Usb 接口案例,UsbInterface usb,既能够接管手机对象,又能够接管相机对象,就体现了接口多态(接口援用能够指向实现了接口的类的对象)。

package com.hspedu.interface_;

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

        // 接口的多态体现
        // 接口类型的变量 if01 能够指向 实现了 IF 接口类的对象实例
        IF if01 = new Monster();
        if01 = new Car();

        // 继承体现的多态
        // 父类类型的变量 a 能够指向 继承 AAA 的子类的对象实例
        AAA a = new BBB();
        a = new CCC();}
}

interface IF {}
class Monster implements IF{}
class Car implements  IF{}

class AAA {

}
class BBB extends AAA {}
class CCC extends AAA {}
2. 多态数组

演示一个案例: 给 **Usb 数组中,寄存 Phone 和相机对象 **,Phone 类还有一个特有的办法 call(),请遍历 Usb 数组,如果是 Phone 对象,除了调用 Usb 接口定义的办法外,还须要调用 Phone 特有办法 call。```java
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("相机工作中...");
    }
}
  1. 接口存在多态传递景象
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() {}
}

外部类

如果定义类在部分地位 (办法中 / 代码块) (1) 部分外部类 (2) 匿名外部类
定义在成员地位 (1) 成员外部类 (2) 动态外部类

根本介绍

一个类的外部又残缺的嵌套了另一个类构造。被嵌套的类称为外部类(inner class), 嵌套其余类的类称为外部类(outer class)。

是咱们类的第五大成员(类的五大成员:属性、办法、结构器、代码块、外部类),外部类最大的特点就是能够间接拜访公有属性,并且能够体现类与类之间的蕴含关系,留神:外部类是学习的难点,同时也是重点,前面看底层源码时, 有大量的外部类。

根本语法

class Outer{ // 外部类
    class Inner{// 外部类}
}
class Other{// 内部其余类}

外部类的分类

定义在外部类部分地位上(比方办法内):

  1. 部分外部类 (有类名)
  2. 匿名外部类 (没有类名,重点!!!!!!!!)

定义在外部类的成员地位上:

  1. 成员外部类 (没用 static 润饰)
  2. 动态外部类 (应用 static 润饰)

部分外部类的应用

阐明:部分外部类是定义在外部类的部分地位,比方办法中,并且有类名

1. 能够间接拜访外部类的所有成员,蕴含公有的。

2. 不能增加拜访修饰符,因为它的位置就是一个局部变量。局部变量是不能应用修饰符的。然而能够应用 final 润饰,因为局部变量也能够应用 final。

3. 作用域:仅仅在定义它的办法或代码块中

4. 部分外部类拜访外部类的成员[拜访形式: 间接拜访]

5. 外部类拜访部分外部类的成员

拜访形式: 创建对象,再拜访 (留神: 必须在作用域内)

小结:

(1)部分外部类定义在办法中 / 代码块
(2) 作用域在办法体或者代码块中
(3) 实质依然是一个类

6. 内部 其余 类不能拜访部分外部类(因为部分外部类位置是一个局部变量)。

7. 如果外部类和部分外部类的成员重名时,默认遵循就近准则,如果想拜访外部类的成员,则能够应用 (外部类名.this. 成员) 去拜访。

这里 外部类名.this 实质上就是外部类的对象,即哪个对象调用了 n2,那么 外部类名.this 就指向哪个对象。

System.out.printin("" 外部类的 n2=”+ 外部类名.this.n2);
package com.hspedu.innerclass;
/**
 * 演示部分外部类的应用
 */
public class LocalInnerClass {//
    public static void main(String[] args) {
        // 演示一遍
        Outer02 outer02 = new Outer02();
        outer02.m1();
        System.out.println("outer02 的 hashcode=" + outer02);
    }
}


class Outer02 {// 外部类
    private int n1 = 100;
    private void m2() {System.out.println("Outer02 m2()");
    }// 公有办法
    public void m1() {// 办法
        //1. 部分外部类是定义在外部类的部分地位, 通常在办法
        //3. 不能增加拜访修饰符, 然而能够应用 final 润饰
        //4. 作用域 : 仅仅在定义它的办法或代码块中
        final class Inner02 {// 部分外部类(实质依然是一个类)
            //2. 能够间接拜访外部类的所有成员,蕴含公有的
            private int n1 = 800;
            public void f1() {//5. 部分外部类能够间接拜访外部类的成员,比方上面 外部类 n1 和 m2()
                //7. 如果外部类和部分外部类的成员重名时,默认遵循就近准则,如果想拜访外部类的成员,//   应用 外部类名.this. 成员)去拜访
                //  Outer02.this 实质就是外部类的对象, 即哪个对象调用了 m1, Outer02.this 就是哪个对象
                System.out.println("n1=" + n1 + "外部类的 n1=" + Outer02.this.n1);
                System.out.println("Outer02.this hashcode=" + Outer02.this);
                m2();}
        }
        //6. 外部类在办法中,能够创立 Inner02 对象,而后调用办法即可
        Inner02 inner02 = new Inner02();
        inner02.f1();}
}

匿名外部类的应用!!!!!

(1)实质是类 (2) 外部类 (3) 该类没有名字 (4) 同时还是一个对象

阐明: 匿名外部类是定义在外部类的部分地位, 比方办法中, 并且没有类名

1. 匿名外部类的根本语法

new 类或接口 (参数列表){类体);
package com.hspedu.innerclass;


/**
 * 演示匿名外部类的应用
 */
public class AnonymousInnerClass {public static void main(String[] args) {Outer04 outer04 = new Outer04();
        outer04.method();}
}

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 tiger = new IA() {
            @Override
            public void cry() {System.out.println("老虎叫唤...");
            }
        };
        System.out.println("tiger 的运行类型 =" + tiger.getClass());
        tiger.cry();
        tiger.cry();
        tiger.cry();

//        IA tiger = new Tiger();
//        tiger.cry();

        // 演示基于!!! 类!!! 的匿名外部类
        // 剖析
        //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 father = new Father("jack"){
            @Override
            public void test() {System.out.println("匿名外部类重写了 test 办法");
            }
        };
        System.out.println("father 对象的运行类型 =" + father.getClass());//Outer04$2
        father.test();

        // 基于!!! 抽象类!!! 的匿名外部类
        Animal animal = new Animal(){
            @Override
            void eat() {System.out.println("小狗吃骨头...");
            }
        };
        animal.eat();}
}

interface IA {// 接口
    public void cry();}
//class Tiger implements IA {
//
//    @Override
//    public void cry() {//        System.out.println("老虎叫唤...");
//    }
//}
//class Dog implements  IA{
//    @Override
//    public void cry() {//        System.out.println("小狗汪汪...");
//    }
//}

class Father { // 类
    public Father(String name) { // 结构器
        System.out.println("接管到 name=" + name);
    }
    public void test() { // 办法}
}

abstract class Animal { // 抽象类
    abstract void eat();}

2. 匿名外部类的语法比拟奇异,因为匿名外部类既是一个类的定义. 同时它自身也是一个对象,因而从语法上看,它既有定义类的特色,也有创建对象的特色,对后面代码剖析能够看出这个特点,因而能够调用匿名外部类办法。

3.能够间接拜访外部类的所有成员,蕴含公有的。

4、不能增加拜访修饰符, 因为它的位置就是一个局部变量。

5. 作用域: 仅仅在定义它的办法或代码块中。

6. 匿名外部类 — 拜访 —-> 外部类成员[拜访形式: 间接拜访]

7. 内部其余类 — 不能拜访 —–> 匿名外部类(因为匿名外部类位置是一个局部变量)

8. 如果外部类和匿名外部类的成员重名时,匿名外部类拜访的话,默认遵循就近准则, 如果想拜访外部类的成员,则能够应用 (外部类名.this. 成员) 去拜访

package com.hspedu.innerclass;

public class AnonymousInnerClassDetail {public static void main(String[] args) {Outer05 outer05 = new Outer05();
        outer05.f1();
        // 内部其余类 --- 不能拜访 -----> 匿名外部类
        System.out.println("main outer05 hashcode=" + outer05);
    }
}

class Outer05 {
    private int n1 = 99;

    public void f1() {
        // 创立一个基于类的匿名外部类
        // 不能增加拜访修饰符, 因为它的位置就是一个局部变量
        // 作用域 : 仅仅在定义它的办法或代码块中
        Person p = new Person(){
            private int n1 = 88;
            @Override
            public void hi() {
                // 能够间接拜访外部类的所有成员,蕴含公有的
                // 如果外部类和匿名外部类的成员重名时,匿名外部类拜访的话,// 默认遵循就近准则,如果想拜访外部类的成员,则能够应用(外部类名.this. 成员)去拜访
                System.out.println("匿名外部类重写了 hi 办法 n1=" + n1 +
                        "内部内的 n1=" + Outer05.this.n1 );
                // Outer05.this 就是调用 f1 的 对象
                System.out.println("Outer05.this hashcode=" + Outer05.this);
            }
        };
        p.hi();// 动静绑定, 运行类型是 Outer05$1

        // 也能够间接调用, 匿名外部类自身也是返回对象
        // class 匿名外部类 extends Person {}
//        new Person(){
//            @Override
//            public void hi() {//                System.out.println("匿名外部类重写了 hi 办法, 哈哈...");
//            }
//            @Override
//            public void ok(String str) {//                super.ok(str);
//            }
//        }.ok("jack");


    }
}

class Person {// 类
    public void hi() {System.out.println("Person hi()");
    }
    public void ok(String str) {System.out.println("Person ok()" + str);
    }
}
// 抽象类 / 接口...

匿名外部类的最佳实际

当做实参间接传递,简洁高效。

package com.hspedu.innerclass;

import com.hspedu.abstract_.AA;

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

        // 当做实参间接传递,简洁高效
        f1(new IL() {
            @Override
            public void show() {System.out.println("这是一副名画~~...");
            }
        });
        // 传统办法
        f1(new Picture());
    }

    // 静态方法, 形参是接口类型
    public static void f1(IL il) {il.show();
    }
}

// 接口
interface IL {void show();
}


// 类 -> 实现 IL => 编程畛域 (硬编码)
class Picture implements IL {

    @Override
    public void show() {System.out.println("这是一副名画 XX...");
    }
}

有一个铃声接口 Bell,外面有个 ring 办法。有一个手机类 Cellphone,具备闹钟性能 alarmclock, 参数是 Bell 类型。测试手机类的闹钟性能, 通过匿名外部类 (对象) 作为参数,打印: 懒猪起床了。再传入另一个匿名外部类(对象),打印: 小伙伴上课了

package com.hspedu.innerclass;

public class InnerClassExercise02 {public static void main(String[] args) {
        /*
        1. 有一个铃声接口 Bell,外面有个 ring 办法。(右图)
        2. 有一个手机类 Cellphone,具备闹钟性能 alarmClock,参数是 Bell 类型(右图)
        3. 测试手机类的闹钟性能,通过匿名外部类 (对象) 作为参数,打印:懒猪起床了
        4. 再传入另一个匿名外部类(对象),打印:小伙伴上课了
         */
        CellPhone cellPhone = new CellPhone();
        // 老韩解读
        //1. 传递的是实现了 Bell 接口的匿名外部类 InnerClassExercise02$1
        //2. 重写了 ring
        //3. Bell bell = new Bell() {
        //            @Override
        //            public void ring() {//                System.out.println("懒猪起床了");
        //            }
        //        }
        cellPhone.alarmClock(new Bell() {
            @Override
            public void ring() {System.out.println("懒猪起床了");
            }
        });

        cellPhone.alarmClock(new Bell() {
            @Override
            public void ring() {System.out.println("小伙伴上课了");
            }
        });
    }
}
interface Bell{ // 接口
    void ring();// 办法}
class CellPhone{// 类
    public void alarmClock(Bell bell){// 形参是 Bell 接口类型
        System.out.println(bell.getClass());
        bell.ring();// 动静绑定}
}

成员外部类的应用

阐明: 成员外部类是定义在外部类的成员地位, 并且没有 static 润饰。

1. 能够间接拜访外部类的所有成员,蕴含公有的。

2. 能够增加任意拜访修饰符 (public、protected、默认、private), 因为它的地
位就是一个成员。

3. 作用域和外部类的其余成员一样,为整个类体比方后面案例,在外部类的成员办法中创立成员外部类对象,再调用办法。

4. 成员外部类 — 拜访 —-> 外部类成员(比方: 属性) 拜访形式: 间接拜访

5. 外部类 — 拜访 ——> 成员外部类(阐明) 拜访形式: 创建对象, 再拜访

6. 内部其余类 — 拜访 —-> 成员外部类

7. 如果外部类和外部类的成员重名时,外部类拜访的话,默认遵循就近准则,如果想拜访外部类的成员,则能够应用 (外部类名.this. 成员) 去拜访

package com.hspedu.innerclass;

public class MemberInnerClass01 {public static void main(String[] args) {Outer08 outer08 = new Outer08();
        outer08.t1();

        // 内部其余类,应用成员外部类的三种形式
        // 第一种形式
        // outer08.new Inner08(); 相当于把 new Inner08()当做是 outer08 成员
        // 这就是一个语法,不要特地的纠结.
        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();
        // 第二形式 在外部类中,编写一个办法,能够返回 Inner08 对象
        Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
        inner08Instance.say();}
}

class Outer08 { // 外部类
    private int n1 = 10;
    public String name = "张三";

    private void hi() {System.out.println("hi()办法...");
    }

    //1. 留神: 成员外部类,是定义在内部内的成员地位上
    //2. 能够增加任意拜访修饰符(public、protected、默认、private), 因为它的位置就是一个成员
    public class Inner08 {// 成员外部类
        private double sal = 99.8;
        private int n1 = 66;
        public void say() {
            // 能够间接拜访外部类的所有成员,蕴含公有的
            // 如果成员外部类的成员和外部类的成员重名,会恪守就近准则.
            //,能够通过  外部类名.this. 属性 来拜访外部类的成员
            System.out.println("n1 =" + n1 + "name =" + name + "外部类的 n1=" + Outer08.this.n1);
            hi();}
    }
    // 办法,返回一个 Inner08 实例
    public Inner08 getInner08Instance(){return new Inner08();
    }


    // 写办法
    public void t1() {
        // 应用成员外部类
        // 创立成员外部类的对象,而后应用相干的办法
        Inner08 inner08 = new Inner08();
        inner08.say();
        System.out.println(inner08.sal);
    }
}

动态外部类的应用

阐明: 动态外部类是定义在外部类的成员地位, 并且 有 static 润饰

1. 能够间接拜访外部类的所有动态成员,蕴含公有的,但不能间接拜访非动态成员。

2. 能够增加任意拜访修饰符(public. protected、默认、private), 因为它的位置就是一个成员。

3. 作用域: 同其余的成员,为整个类体。

4. 动态外部类 — 拜访 —-> 外部类(比方: 动态属性)[拜访形式: 间接拜访所有动态成员]。

5.外部类 — 拜访 ——> 动态外部类 拜访形式: 创建对象,再拜访。

6.内部其余类 — 拜访 —–> 动态外部类。

7. 如果外部类和动态外部类的成员重名时,动态外部类拜访的时,默认遵循就近准则,如果想拜访外部类的成员,则能够应用 (外部类名. 成员) 去访向。

package com.hspedu.innerclass;

public class StaticInnerClass01 {public static void main(String[] args) {Outer10 outer10 = new Outer10();
        outer10.m1();

        // 内部其余类 应用动态外部类
        // 形式 1
        // 因为动态外部类,是能够通过类名间接拜访(前提是满足拜访权限)
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();
        // 形式 2
        // 编写一个办法,能够返回动态外部类的对象实例.
        Outer10.Inner10 inner101 = outer10.getInner10();
        System.out.println("============");
        inner101.say();

        Outer10.Inner10 inner10_ = Outer10.getInner10_();
        System.out.println("************");
        inner10_.say();}
}

class Outer10 { // 外部类
    private int n1 = 10;
    private static String name = "张三";
    private static void cry() {}
    //Inner10 就是动态外部类
    //1. 放在外部类的成员地位
    //2. 应用 static 润饰
    //3. 能够间接拜访外部类的所有动态成员,蕴含公有的,但不能间接拜访非动态成员
    //4. 能够增加任意拜访修饰符(public、protected、默认、private), 因为它的位置就是一个成员
    //5. 作用域:同其余的成员,为整个类体
    static class Inner10 {
        private static String name = "Timerring";
        public void say() {
            // 如果外部类和动态外部类的成员重名时,动态外部类拜访的时,// 默认遵循就近准则,如果想拜访外部类的成员,则能够应用(外部类名. 成员)System.out.println(name + "外部类 name=" + Outer10.name);
            cry();}
    }

    public void m1() { // 外部类 --- 拜访 ------> 动态外部类 拜访形式:创建对象,再拜访
        Inner10 inner10 = new Inner10();
        inner10.say();}

    public Inner10 getInner10() {return new Inner10();
    }

    public static Inner10 getInner10_() {return new Inner10();
    }
}

课堂测试题

判断输入:

package com.hspedu.innerclass;

public class InnerClassExercise {public static void main(String[] args) {}}

class Test {// 外部类

    public Test() {// 结构器
        Inner s1 = new Inner();
        s1.a = 10;
        Inner s2 = new Inner();
        System.out.println(s2.a);
    }

    class Inner { // 外部类,成员外部类
        public int a = 5;
    }

    public static void main(String[] args) {Test t = new Test();
        Inner r = t.new Inner();//5
        System.out.println(r.a);//5
    }
}

文章和代码曾经归档至【Github 仓库:https://github.com/timerring/java-tutorial】或者公众号【AIShareLab】回复 java 也可获取。

退出移动版