Java笔记、第九章、面向对象编程(高级部分)

第九章、面向对象编程(高级部分)

9.1、类变量和类方法(P374~P382)

9.1.1、类变量引出(P374)

  • 类变量 - 提出问题

有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在有多少人在玩?编写程序解决。

  • 传统的方法来解决

思路:

1、在main方法中定义一个变量count

2、当一个小孩加入堆雪人后count++,最后count就记录了有多少个小孩在玩堆雪人

package com.hspedu.static_;
​
public class ChildGame {
    public static void main(String[] args) {
        int count = 0;
        Child child = new Child("Mike");
        child.join();
        count++;
        Child child1 = new Child("John");
        child1.join();
        count++;
        Child tom = new Child("Tom");
        tom.join();
        count++;
​
        System.out.println("共有" + count + "个小孩加入了堆雪人");
    }
}
​
class Child {
    public String name;
​
    public Child(String name) {
        this.name = name;
    }
​
    public void join() {
        System.out.println(name + "加入了堆雪人");
    }
}
​

输出结果为:

Mike加入了堆雪人
John加入了堆雪人
Tom加入了堆雪人
共有3个小孩加入了堆雪人
​

  • 问题分析

1、count是独立于对象的变量,很尴尬

2、以后我们访问count很麻烦

3、因此,我们引出 类变量/静态变量

9.1.2、类变量快速入门(P375)

通过上面问题思考:如果设计一个int count表示总人数,我们在创建一个小孩时,就把count加1,并且count是所有对象共享的就ok了,我们使用类变量来解决。

  • 改善上面代码

package com.hspedu.static_;
​
public class ChildGame {
    public static void main(String[] args) {
        //int count = 0;
        Child child = new Child("Mike");
        child.join();
        //count++;
        child.count++;
​
        Child child1 = new Child("John");
        child1.join();
        //count++;
        child1.count++;
​
        Child tom = new Child("Tom");
        tom.join();
        //count++;
        tom.count++;
​
        System.out.println("共有" + Child.count + "个小孩加入了堆雪人");
​
        //下面代码会输出什么结果
        System.out.println("共有" + child.count + "个小孩加入了堆雪人");
        System.out.println("共有" + child1.count + "个小孩加入了堆雪人");
        System.out.println("共有" + tom.count + "个小孩加入了堆雪人");
    }
}
​
class Child {
    //定义一个类变量(静态变量 static)count
    //该变量最大的特点就是会被Child 类的所有的对象实例共享
    public static int count = 0;
    public String name;
​
    public Child(String name) {
        this.name = name;
    }
​
    public void join() {
        System.out.println(name + "加入了堆雪人");
    }
}
​

输出结果为:

Mike加入了堆雪人
John加入了堆雪人
Tom加入了堆雪人
共有3个小孩加入了堆雪人
共有3个小孩加入了堆雪人
共有3个小孩加入了堆雪人
共有3个小孩加入了堆雪人
​

9.1.3、类变量内存剖析(P376)

①静态变量被对象共享

②因此不影响对静态变量的使用

在jdk7或者8以前,静态变量是放在方法区的静态域中的;在jdk8以后是放在堆里面的,在对堆里面会通过反射机制,会加载class对象,在对象最后,会把静态变量放在class对象中

9.1.4、类变量定义访问(P377)

  • 什么是类变量

类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。这个从前面的图也可看出来。

  • 如何定义类变量

定义语法:

访问修饰符 static 数据类型 变量名;//[推荐这种类型]
static 访问修饰符 数据类型 变量名;

  • 如何访问类变量

类名.类变量名;//[推荐使用]
//或者
对象名.类变量名;

静态变量的访问修饰符的访问权限和范围与普通属性是一样的。

代码演示:

package com.hspedu.static_;
​
public class VisitStatic {
    public static void main(String[] args) {
        //类名.类变量名;//[推荐使用]
        //说明:类变量是随着类的加载而创建的,所以即使没有创建对象实例也可以访问
        System.out.println(AAA.str);
        System.out.println(AAA.getString_());
​
        //对象名.类变量名;
        AAA aaa = new AAA();
        System.out.println(aaa.str);
        System.out.println(aaa.getString_());
​
    }
}
class AAA{
    public static String str = "韩顺平教育";
    private static String string_ = "我我我";
​
    public static String getString_() {
        return string_;
    }
​
    public static void setString_(String string_) {
        AAA.string_ = string_;
    }
}
​

输出结果为:

韩顺平教育
我我我
韩顺平教育
我我我
​

9.1.5、类变量使用细节(P378)

  • 类变量使用注意事项和细节讨论

1、什么时候需要用类变量?

当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学术类,统计所有学生共交多少钱。Student(name,static fee)

2、类变量与实例变量(普通属性)区别

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

3、加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量

4、类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但Java设计者推荐我们使用 类名.类变量名 形式访问[前提是 满足访问修饰符的访问权限和范围]。

5、实例变量不能通过 类名.类变量名 方式访问

6、类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了。

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

9.1.6、类方法快速入门(P379)

  • 类方法基本介绍

类方法也叫静态方法。

创建语法如下:

访问修饰符 static 数据返回类型 方法名(){}//[推荐]
static 访问修饰符 数据返回类型 方法名(){}

  • 类方法的调用

调用语法如下[前提是 满足访问修饰符的访问权限和范围]:

类名.类方法名;
//或者
对象名.类方法名

  • 类方法的使用

如:统计学费总和

代码实现:

package com.hspedu.static_;
​
public class StaticMethod {
    public static void main(String[] args) {
        //学生1的费用
        Stu mary = new Stu("Mary", 100);
        System.out.println(mary);
        mary.payFee(mary.getStudentFee());//调用方法1
        //Stu.payFee(mary.getStudentFee());//调用方法2
​
        //学生2的费用
        Stu john = new Stu("John", 200);
        System.out.println(john);
        john.payFee(john.getStudentFee());//调用方法1
        //Stu.payFee(john.getStudentFee());//调用方法2
​
        //查询学费总和
        Stu.showFee();
​
        //下面输出结果为
        mary.showFee();
    }
}
​
class Stu {
    private String name;//普通成员
    private double studentFee;//记录单个学生费用
    //定义一个静态变量,来累计学生的学费
    private static double studentSumFee = 0;
​
    public Stu(String name, double studentFee) {
        this.name = name;
        this.studentFee = studentFee;
    }
​
    //
    public static void payFee(double fee) {
        Stu.studentSumFee += fee;//累计和
    }
​
    public static void showFee() {
        System.out.println("总学费为:" + Stu.studentSumFee);
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public double getStudentFee() {
        return studentFee;
    }
​
    public void setStudentFee(double studentFee) {
        this.studentFee = studentFee;
    }
​
    @Override
    public String toString() {
        return "Stu{" +
                "name='" + name + '\'' +
                ", studentFee=" + studentFee +
                '}';
    }
}
​

输出结果为:

Stu{name='Mary', studentFee=100.0}
Stu{name='John', studentFee=200.0}
总学费为:300.0
总学费为:300.0
​

9.1.7、类方法最佳实践(P380)

如果我们希望不创建实例方法,也可以调用某个方法(即当作工具来使用);这时,把方法组成静态方法是非常合适的。

如:

package com.hspedu.static_;
​
public class StaticMethod {
    public static void main(String[] args) {
        //调用自己的工具
        System.out.println(MyTools.calSum(20, 30));
    }
}
​
class MyTools {
    public static double calSum(double num1, double num2) {
        return num1 + num2;
    }
}
​

输出结果为:

50.0
​

小结:

在程序员实际开发中,往往会将一些通用的方法,设计成静态方法,这样我们就不需要创建对象就可以使用;比如打印一维数组,冒泡排序,完成某个计算任务等等。

9.1.8、类方法注意事项(P381)

(1)类方法和普通方法都是随着类的加载而加载的,将结构信息存储在方法区。

(2)类方法可以通过类名调用,也可以通过对象名调用。

(3)普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用

(4)类方法中不允许使用和对象有关的关键字,比如this和super。普通方法可以。

package com.hspedu.static_;
​
public class StaticMethodDetail {
    public static void main(String[] args) {
    }
}
​
class WWW {
    private int num = 0;
​
    public static void B() {
        //(4)类方法中不允许使用和对象有关的关键字,比如this和super。普通方法可以。
        System.out.println(this.num);//这里直接报错
    }
}
​

(5)类方法(静态方法)中,只能访问 静态变量静态方法

package com.hspedu.static_;
​
public class StaticMethodDetail {
    public static void main(String[] args) {
    }
}
​
class WWW {
    private int num1 = 0;
    private static int num2 = 1;
​
    public void A() {
​
    }
​
    public static void B() {
        //(4)类方法中不允许使用和对象有关的关键字,比如this和super。普通方法可以。
        //System.out.println(this.num);//这里直接报错
    }
​
    public static void C() {
        //(5)类方法(静态方法)中,只能访问 静态变量 或 静态方法。
        //静态方法只能访问静态成员
        System.out.println(num2);//正确
        System.out.println(WWW.num2);//正确
        //System.out.println(this.num2);//错误
​
        B();//正确
        WWW.B();//正确
​
        //A();//错误,A()方法为非静态方法
    }
}
​

(6)普通成员方法,既可以访问 非静态成员,也可以访问静态成员。

package com.hspedu.static_;
​
public class StaticMethodDetail {
    public static void main(String[] args) {
    }
}
​
class WWW {
    private int num1 = 0;
    private static int num2 = 1;
​
    public void A() {
​
    }
​
    public static void B() {
        //(4)类方法中不允许使用和对象有关的关键字,比如this和super。普通方法可以。
        //System.out.println(this.num);//这里直接报错
    }
​
    public static void C() {
        //(5)类方法(静态方法)中,只能访问 静态变量 或 静态方法。
        //静态方法只能访问静态成员
        System.out.println(num2);//正确
        System.out.println(WWW.num2);//正确
        //System.out.println(this.num2);//错误
​
        B();//正确
        WWW.B();//正确
​
        //A();//错误,A()方法为非静态方法
    }
​
    //(6)普通成员方法,既可以访问 非静态成员,也可以访问静态成员。
    public void DD() {
        //非静态成员
        System.out.println(num1);
        A();
​
        //静态成员
        System.out.println(num2);
        B();
        C();
    }
}
​

小结:

静态的方法只能访问静态的成员;非静态的方法,既可以访问静态成员,也可以访问非静态成员。

9.1.9、类成员课堂练习(P382)

1、下面代码输出结果是什么?

main方法:

package com.hspedu.static_;
​
public class StaticExercise01 {
    public static void main(String[] args) {
        new Test().count();
        new Test().count();
        System.out.println(Test.count);
    }
}
​

Test类:

package com.hspedu.static_;
​
public class Test {
    static int count = 9;
​
    public void count() {
        System.out.println("count = " + count++);
    }
}

输出结果为:

count = 9
count = 10
11
​

2、看下面代码有没有错误,如果有就注释,看看输出什么。

package com.hspedu.static_;
​
public class StaticExercise02 {
    public static void main(String[] args) {
        System.out.println("Number of total is " + Person.getTotalPerson());//0
        Person person = new Person();
        System.out.println("Number of total is " + person.getTotalPerson());//1
        System.out.println("Number of total is " + Person.getTotalPerson());//1
    }
}
​
class Person {
    private int id;
    private static int total = 0;
​
    public static int getTotalPerson() {
        //id++;
        return total;
    }
​
    public Person() {
        total++;
        id = total;
    }
}
​

输出结果为:

Number of total is 0
Number of total is 1
Number of total is 1
​

3、看下面代码有没有错误,如果有就注释,看看total结果为多少。

package com.hspedu.static_;
​
public class StaticExercise03 {
    public static void main(String[] args) {
        Persons.setTotalPersons(3);
        new Persons();
        
        System.out.println(Persons.getTotal());
    }
}
​
class Persons {
    private int id;
    private static int total = 0;
​
    public static void setTotalPersons(int total) {
        //this.total = total;//想把形参里的total赋值给静态变量total,这种方法是错误的,下面这种方法正确
        Persons.total = total;
    }
​
    public Persons() {
        total++;
        id = total;
    }
​
    public static int getTotal() {
        return total;
    }
​
    public static void setTotal(int total) {
        Persons.total = total;
    }
}
​

输出结果为:

4
​

9.2、理解main方法语法(P383~P385)

9.2.1、main语法说明(P383)

解释main方法的形式:

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

1、main方法是虚拟机调用

2、java虚拟机需要调用类的main方法,所以该方法的访问权限必须是public

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

4、该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数

5、java执行的程序 参数1 参数2 参数3

代码:

public class waste {
    public static void main(String[] args) {
    
        for (int i = 0; i < args.length; i++) {
            System.out.println("第" + (i + 1) + "个参数" + args[i]);
        }
    }
}

输出结果为:(下面图片的A,B,C,对应着上面图片的第一个参数,第二个参数,第三个参数)

9.2.2、main特别说明(P384)

1、在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性。

2、但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。

package com.hspedu.main_;
​
import sun.applet.Main;
​
public class Main01 {
    private static int num1 = 999;
    private int num2 = 222;
​
    public static void PrintNum1() {
        System.out.println("静态变量num1 = " + num1);
        //System.out.println("非静态变量num2 = " + num2);//会报错
    }
​
    public void PrintNum2() {
        System.out.println("静态变量num1 = " + num1);
        System.out.println("非静态变量num2 = " + num2);
    }
​
    public static void main(String[] args) {
        //静态成员
        System.out.println(num1);
        PrintNum1();
​
        //非静态成员
        //System.out.println(num2);//会报错
        //PrintNum2();//会报错
​
        //正确调用非静态成员
        Main01 main01 = new Main01();
        System.out.println(main01.num2);
        main01.PrintNum2();
    }
}
​

输出结果为:

999
静态变量num1 = 999
222
静态变量num1 = 999
非静态变量num2 = 222
​

9.2.3、main动态传值(P385)

先运行下面程序,什么都不会输出

package com.hspedu.main_;
​
public class Main02 {
    public static void main(String[] args) {
        //先运行一下程序
        //快点 上号 来来来 我 你 他
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
    }
}
​

截图1

 截图2

最后再次运行上面代码,输出结果为:

快点
上号
来来来
我
你
他
​

9.3、代码块(P386~391)

9.3.1、代码块快速入门(P386)

  • 基本介绍

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

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

  • 基本语法

[修饰符]{
    代码
};

说明注意:

1、修饰符可以不写;要写的话,也只能写static

2、代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块。

3、逻辑语句可以分为任何逻辑语句(输入,输出、方法调用、循环、判断等)

4、最后的 " ; " 可以写上,也可以省略

  • 代码块的好处和案例演示

1、相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作

2、场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的复用性。

package com.hspedu.codeblock_;
​
public class CodeBlock01 {
    public static void main(String[] args) {
        new Movie("成龙历险记");
        System.out.println("-----------------------------------");//分割线
        new Movie("喜羊羊与灰太狼", 20);
        System.out.println("-----------------------------------");//分割线
        new Movie("果宝特攻", 30, "陈奋、王巍");
    }
}
​
class Movie {
    private String name;
    private double price;
    private String director;
​
    //创建3个构造器
    //在每个构造器中添加相同的语句
    public Movie(String name) {
//        System.out.println("电影屏幕打开...");
//        System.out.println("广告开始...");
//        System.out.println("电影正式开始播放...");
        this.name = name;
    }
​
    public Movie(String name, double price) {
//        System.out.println("电影屏幕打开...");
//        System.out.println("广告开始...");
//        System.out.println("电影正式开始播放...");
        this.name = name;
        this.price = price;
    }
​
    public Movie(String name, double price, String director) {
//        System.out.println("电影屏幕打开...");
//        System.out.println("广告开始...");
//        System.out.println("电影正式开始播放...");
        this.name = name;
        this.price = price;
        this.director = director;
    }
​
    // (1)上面三个构造器都有相同的语句,把他们注释
    // (2)这样看起来比较冗余
    // (3)这时我们可以把相同的语句,放到一个代码块中
    // (4)这样我们不管先调用哪个构造器,都会先调用代码块中的内容
    // (5)运行程序后,会发现,代码块调用的优先级高于构造器
    {
        System.out.println("电影屏幕打开...");
        System.out.println("广告开始...");
        System.out.println("电影正式开始播放...");
    };//这里分号写不写均可
}
​

输出结果为:

电影屏幕打开...
广告开始...
电影正式开始播放...
-----------------------------------
电影屏幕打开...
广告开始...
电影正式开始播放...
-----------------------------------
电影屏幕打开...
广告开始...
电影正式开始播放...
​

9.3.2、代码块使用细节_1(P387)

  • 代码块使用注意事项和细节讨论

1、static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行。

2、类什么时候被加载[重点]

①创建对象实例时(new)

②创建子类对象实例,父类也会被加载

③使用类的静态成员时(静态属性,静态方法)

案例演示:

package com.hspedu.codeblock_;
​
public class CodeBlockDetail01 {
    public static void main(String[] args) {
        //static代码块,它随着类的加载而执行,并且只会执行一次。
​
        //类被加载有3种情况
        //①创建对象实例时(new)
        AA aa = new AA();//注释掉这一行,输出结果不变
​
        //②创建子类对象实例,父类也会被加载
        BB bb = new BB();//再注释掉这一行,输出结果不变
​
        //③使用类的静态成员时(静态属性,静态方法)
        System.out.println(BB.n1);
        System.out.println(BB.n2);
    }
}
​
class AA {
    public static int n2 = 666;
​
    static {
        System.out.println("AA 的代码块被调用");
    }
}
​
class BB extends AA {
    public static int n1 = 99;
​
    static {
        System.out.println("BB 的代码块被调用");
    }
}
​

输出结果为:

AA 的代码块被调用
BB 的代码块被调用
99
666
​

3、①普通的代码块,在创建对象实例时,会被隐式的调用;被创建一次,就会调用一次。

package com.hspedu.codeblock_;
​
public class CodeBlockDetail01 {
    public static void main(String[] args) {
        //2、static代码块,它随着类的加载而执行,并且只会执行一次。
        //类被加载有3种情况
        //①创建对象实例时(new)
        AA aa = new AA();//注释掉这一行,输出结果不变
​
        //②创建子类对象实例,父类也会被加载
        BB bb = new BB();//再注释掉这一行,输出结果不变
​
        //③使用类的静态成员时(静态属性,静态方法)
        System.out.println(BB.n1);
        System.out.println(BB.n2);
​
​
        System.out.println("-----------------------------------");//分割线
        //3、普通的代码块,在创建对象实例时,会被隐式的调用;被创建一次,就会调用一次。
        new DD();
        new DD();//这里静态代码块被调用一次,普通代码块被调用两次
​
        // 如果只是使用类的静态成员时,普通代码块并不会执行。
    }
}
​
class AA {
    public static int n2 = 666;
​
    static {
        System.out.println("AA 的代码块被调用");
    }
}
​
class BB extends AA {
    public static int n1 = 99;
​
    static {
        System.out.println("BB 的代码块被调用");
    }
}
​
class DD {
    public static int num3 = 123;
    static {
        System.out.println("静态代码块被调用");
    }
​
    {
        System.out.println("普通代码块被调用");
    }
}
​

输出结果为:

AA 的代码块被调用
BB 的代码块被调用
99
666
-----------------------------------
静态代码块被调用
普通代码块被调用
普通代码块被调用
​

②如果只是使用类的静态成员时,普通代码块并不会执行。

package com.hspedu.codeblock_;
​
public class CodeBlockDetail01 {
    public static void main(String[] args) {
        //2、static代码块,它随着类的加载而执行,并且只会执行一次。
        //类被加载有3种情况
        //①创建对象实例时(new)
        AA aa = new AA();//注释掉这一行,输出结果不变
​
        //②创建子类对象实例,父类也会被加载
        BB bb = new BB();//再注释掉这一行,输出结果不变
​
        //③使用类的静态成员时(静态属性,静态方法)
        System.out.println(BB.n1);
        System.out.println(BB.n2);
​
​
        System.out.println("-----------------------------------");//分割线
        //3、普通的代码块,在创建对象实例时,会被隐式的调用;被创建一次,就会调用一次。
//        new DD();
//        new DD();//这里静态代码块被调用一次,普通代码块被调用两次
​
        // 如果只是使用类的静态成员时,普通代码块并不会执行。
        System.out.println(DD.num3);
        DD.PrintDD();//还是不会调用普通代码块
    }
}
​
class AA {
    public static int n2 = 666;
​
    static {
        System.out.println("AA 的代码块被调用");
    }
}
​
class BB extends AA {
    public static int n1 = 99;
​
    static {
        System.out.println("BB 的代码块被调用");
    }
}
​
class DD {
    public static int num3 = 123;
​
    static {
        System.out.println("静态代码块被调用");
    }
​
    {
        System.out.println("普通代码块被调用");
    }
​
    public static void PrintDD() {
        System.out.println("DD中的PrintDD方法被调用");
    }
}
​

输出结果为:

AA 的代码块被调用
BB 的代码块被调用
99
666
-----------------------------------
静态代码块被调用
123
DD中的PrintDD方法被调用
​

9.3.2、代码使用细节_2(P388)

4、创建对象时,在一个类 调用的顺序是①②③(重点,难点):

①调用静态代码块和静态属性初始化

静态代码块和静态属性初始化调用的优先级一样;如果有多个代码块和多个静态变量初始化,则按他们定义的顺序调用。

package com.hspedu.codeblock_;
​
public class CodeBlockDetail02 {
    public static void main(String[] args) {
        new A();
    }
}
​
class A {
    private static int n1 = getN1();
​
    static {
        System.out.println("A类的代码块被调用");
    }
​
    public static int getN1() {
        System.out.println("A类的getN1()方法被调用");
        return 10;
    }
​
    public static void setN1(int n1) {
        A.n1 = n1;
    }
}
​

输出结果为:

A类的getN1()方法被调用
A类的代码块被调用
​

②调用普通代码块和普通属性的初始化

普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用。

package com.hspedu.codeblock_;
​
public class CodeBlockDetail02 {
    public static void main(String[] args) {
        new A();
    }
}
​
class A {
    private int n2 = getN2();
    {
        System.out.println("A类的普通代码块被调用");
    }
​
    private static int n1 = getN1();
​
    static {
        System.out.println("A类的静态代码块被调用");
    }
​
    public static int getN1() {
        System.out.println("A类的getN1()方法被调用");
        return 10;
    }
​
    public static void setN1(int n1) {
        A.n1 = n1;
    }
​
    public int getN2() {
        System.out.println("A类的getN2()被调用");
        return 200;
    }
}
​

输出结果为:

A类的getN1()方法被调用
A类的静态代码块被调用
A类的getN2()被调用
A类的普通代码块被调用
​

③调用构造方法

package com.hspedu.codeblock_;
​
public class CodeBlockDetail02 {
    public static void main(String[] args) {
        new A();
    }
}
​
class A {
    private int n2 = getN2();
​
    {
        System.out.println("A类的普通代码块被调用");
    }
​
    private static int n1 = getN1();
​
    static {
        System.out.println("A类的静态代码块被调用");
    }
​
    public static int getN1() {
        System.out.println("A类的getN1()方法被调用");
        return 10;
    }
​
    public static void setN1(int n1) {
        A.n1 = n1;
    }
​
    public int getN2() {
        System.out.println("A类的getN2()被调用");
        return 200;
    }
​
    public A() {
        System.out.println("A类的无参构造器被调用");
    }
}
​

输出结果为:

A类的getN1()方法被调用
A类的静态代码块被调用
A类的getN2()被调用
A类的普通代码块被调用
A类的无参构造器被调用
​

9.3.3、代码使用细节_3(P389)

5、构造器的最前面其实隐含了 super()和调用普通代码块,新写一个类演示,静态相关的代码,属性初始化,在类加载时就执行完毕,因此是优先于构造器和普通代码块执行的。

package com.hspedu.codeblock_;
​
public class CodeBlockDetail03 {
    public static void main(String[] args) {
        new BBB();
    }
}
​
class AAA {
    public AAA() {
        //super();
        //调用本类的普通代码块
        System.out.println("AAA无参构造器被调用");//2
    }
​
    {
        System.out.println("AAA的普通代码块被调用");//1
    }
}
​
class BBB extends AAA{
    public BBB() {
        //super();
        //调用本类的普通代码块
        System.out.println("BBB无参构造器被调用");//4
    }
​
    {
        System.out.println("BBB的普通代码块被调用");//3
    }
}
​

输出结果为:

AAA的普通代码块被调用
AAA无参构造器被调用
BBB的普通代码块被调用
BBB无参构造器被调用
​

9.3.4、代码使用细节_4(P390)

6、我们看一下创建一个子类对象时(继承关系),他们的静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序如下:

①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)

②子类的静态代码块和静态属性(优先级一样,按定义顺序执行)

③父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

④父类的构造方法

⑤子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

⑥子类的构造方法//面试题

package com.hspedu.codeblock_;
​
import com.sun.xml.internal.bind.v2.TODO;
​
public class CodeBlockDetail04 {
    public static void main(String[] args) {
//老师说明
//(1) 进行类的加载
//1.1 先加载 父类 A02 1.2 再加载 B02
//(2) 创建对象
//2.1 从子类的构造器开始
        new B02();//对象
​
    }
}
​
class A02 { //父类
    private static int n1 = getVal01();
​
    static {
        System.out.println("A02 的一个静态代码块..");//(2)
    }
​
    {
        System.out.println("A02 的第一个普通代码块..");//(5)
    }
​
    public 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 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)
​
    }
}
​

输出结果为:

getVal01
A02 的一个静态代码块..
getVal03
B02 的一个静态代码块..
A02 的第一个普通代码块..
getVal02
A02 的构造器
getVal04
B02 的第一个普通代码块..
B02 的构造器
​

7、静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员

package com.hspedu.codeblock_;
​
public class CodeBlockDetail04 {
    public static void main(String[] args) {
        new C02();
    }
}
​
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();
    }
}
​

输出结果为:

200
100
200
​

9.3.5、代码块课堂练习(P391)

1、下面代码输出什么?

package com.hspedu.codeblock_;
​
public class CodeBlockExercise01 {
    public static void main(String[] args) {
        System.out.println("total = " + Person.total);//(1)调用静态变量 (5)打印total的值
        System.out.println("total = " + Person.total);//(6)打印total的值
    }
}
​
class Person {
    public static int total;//(2)先创建静态变量
​
    static {
        total = 100;//(3)给total赋值
        System.out.println("int static block!");//(4)输出这句话,返回
    }
}
​

输出结果为:

int static block!
total = 100
total = 100
​

2、下面代码输出什么?

package com.hspedu.codeblock_;
​
public class CodeBlockExercise02 {
    public static void main(String[] args) {
        Test test = new Test();
    }
}
​
class Test {
    Sample sam1 = new Sample("sam1成员初始化");//(3)调用Sample类有参构造器输出这句话
    static Sample sam = new Sample("静态成员sam初始化");//(1)由于为静态成员,先创建,调用Sample类有参构造器先输出这句话
​
    static {
        System.out.println("static块执行");//(2)输出这句话
        if (sam == null)
            System.out.println("sam is null");
    }
​
    Test() {
        System.out.println("Test默认构造函数被调用");
    }
}
​
class Sample {
    Sample(String s) {
        System.out.println(s);
    }
​
    Sample() {
        System.out.println("Sample默认构造函数被调用");
    }
}
​

输出结果为:

静态成员sam初始化
static块执行
sam1成员初始化
Test默认构造函数被调用
​

9.4、单例设计模式(P392~393)

9.4.1、单例模式饿汉式(P392)

1、静态方法和属性的经典作用

2、设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索

  • 什么是单例模式

单例(单个的实例)

1、所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例并且该类只能提供一个取得其对象实例的方法

2、单例模式有两种方式:①饿汉式②懒汉式

  • 单例模式应用实例

演示饿汉式与懒汉式单例模式的实现

步骤如下:

①构造器私有化 =====》防止直接new

②类的内部创建对象

③向外暴露一个静态的公共方法。getInstance,Instance:实例

④饿汉式中有属性被调用,其他静态属性即使不使用,也会创建,占用存储,会造成资源浪费

代码实现:

(饿汉式)

package com.hspedu.codeblock_;
​
public class SingleTon01 {
    public static void main(String[] args) {
        GirlFriend girlFriend = GirlFriend.get123();
        System.out.println(girlFriend);
​
        GirlFriend girlFriend1 = GirlFriend.get123();
        System.out.println(girlFriend1);
​
        System.out.println(girlFriend == girlFriend1);//true,说明为同一个对象
    }
}
​
class GirlFriend {
    private String name;
​
    //2、类的内部创建对象
    private static GirlFriend gf = new GirlFriend("ggb");
​
    //1、构造器私有化
    private GirlFriend(String name) {
        this.name = name;
    }
​
    //3、向外暴露一个静态的公共方法。
    //为了能够在静态方法中,返回 gf 对象,需要将其修饰为static
    public static GirlFriend get123() {
        return gf;
    }
​
    @Override
    public String toString() {
        return "GirlFriend{" +
                "name='" + name + '\'' +
                '}';
    }
}
​

输出结果为:

GirlFriend{name='ggb'}
GirlFriend{name='ggb'}
true
​

9.4.2、单例模式懒汉式(P393)

(2)懒汉式

步骤如下:

①构造器私有化 =====》防止直接new

②类的内部创建对象

③向外暴露一个静态的公共方法。

package com.hspedu.single_;
​
public class SingleTon02 {
    public static void main(String[] args) {
        System.out.println(Cat.n1);//并不会调用构造器
​
        Cat cat = Cat.getCat();
        System.out.println(cat);
    }
}
​
class Cat {
    private String name;
    public static int n1 = 100;
​
    //②类的内部创建对象
    private static Cat cat;
​
    //①构造器私有化
    private Cat(String name) {
        System.out.println("构造器被调用");
        this.name = name;
    }
​
    //③向外暴露一个静态的公共方法。
    public static Cat getCat() {
        if (cat == null) {
            cat = new Cat("小白");
        }
        return cat;
    }
​
    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}
​

输出结果为:

100
构造器被调用
Cat{name='小白'}
​

  • 饿汉式VS懒汉式

1、二者最主要区别在于创建对象的时机不同:饿汉式实在类加载就创建了对象实例,而懒汉式是在使用时才创建。

2、饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后面学习线程,再进行完善)

3、饿汉式存在浪费资源的可能。如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了;懒汉式是使用时才创建,就不存在这个问题。

9.5、final关键字(P394~397)

9.5.1、final基本使用(P395)

  • 基本介绍 final:最后的,最终的

final 可以修饰类、属性、方法和局部变量

final成员变量表示常量,只能被赋值一次,赋值后值不再改变。

当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;

如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。

本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。 final修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。

加粗原文链接:浅析Java中的final关键字(详细)_java final关键字-CSDN博客

​​​​​​​

在某些情况下,程序员可能有以下需求,就会使用到final:

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

package com.hspedu.final_;
​
public class Final01 {
    public static void main(String[] args) {
​
    }
}
​
//如果不希望A类被其他类继承,就加final修饰A类
final class A {
​
}
//class B extends A {//这里会报错
//
//}
​

②当不希望父类的某个方法被子类覆盖/重写时,可以用final关键字修饰

package com.hspedu.final_;
​
public class Final01 {
    public static void main(String[] args) {
​
    }
}
​
//如果不希望A类被其他类继承,就加final修饰A类
final class A {
​
}
//class B extends A {//这里会报错
//
//}
​
class C {
    //如果我们要求hi不能被子类重写
    //可以使用final修饰hi方法
    public final void hi() {
        
    }
}
class D extends C{
//    @Override
//    public void hi() {//父类加上final,这里就不能重写hi()方法,会报错
//        super.hi();
//    }
}
​

③当不希望类的某个属性的值被修改,可以用final修饰

package com.hspedu.final_;
​
public class Final01 {
    public static void main(String[] args) {
//        //变量TAX_RATE没有使用final修饰时,可以修改值
//        E e = new E();
//        e.TAX_RATE = 0.09;//final修饰后,这里会报错
​
    }
}
​
//如果不希望A类被其他类继承,就加final修饰A类
final class A {
​
}
//class B extends A {//这里会报错
//
//}
​
class C {
    //如果我们要求hi不能被子类重写
    //可以使用final修饰hi方法
    public final void hi() {
​
    }
}
​
class D extends C {
//    @Override
//    public void hi() {//父类加上final,这里就不能重写hi()方法,会报错
//        super.hi();
//    }
}
​
//③当不希望类的某个属性的值被修改,可以用final修饰
class E {
    public final double TAX_RATE = 0.08;
}
​

④当不希望某个局部变量被修改,可以使用final修饰

package com.hspedu.final_;
​
public class Final01 {
    public static void main(String[] args) {
//        //变量TAX_RATE没有使用final修饰时,可以修改值
//        E e = new E();
//        e.TAX_RATE = 0.09;//final修饰后,这里会报错
​
    }
}
​
//如果不希望A类被其他类继承,就加final修饰A类
final class A {
​
}
//class B extends A {//这里会报错
//
//}
​
class C {
    //如果我们要求hi不能被子类重写
    //可以使用final修饰hi方法
    public final void hi() {
​
    }
}
​
class D extends C {
//    @Override
//    public void hi() {//父类加上final,这里就不能重写hi()方法,会报错
//        super.hi();
//    }
}
​
//③当不希望类的某个属性的值被修改,可以用final修饰
class E {
    public final double TAX_RATE = 0.08;
}
​
//④当不希望某个局部变量被修改,可以使用final修饰
class F {
    {
        final double TAX_RATE = 0.08;
//        TAX_RATE = 0.09;//变量名没用final修饰,可以改值,加上final后,就会报错
        
    }
​
}

9.5.2、final使用细节_1(P396)

  • final使用注意事项和细节讨论

1、final修饰的属性又叫常量,一般 用 XX_XX_XX 来命名

2、final修饰的属性在定义时,必须赋初值,并且以后不能再更改,赋值可以在如下位置之一[选择一个位置赋初值即可]

①定义时:

如: public final double TAX_RATE=0.08;

package com.hspedu.final_;
​
public class FinalDetail {
}
​
class AA {
    public final double TAX_RATE = 0.08;
}
​

②在构造器中

package com.hspedu.final_;
​
public class FinalDetail {
}
​
class AA {
    //public final double TAX_RATE = 0.08;
    public final double TAX_RATE2;
​
    public AA() {
        TAX_RATE2 = 6.6;
    }
}
​

③在代码块中,修饰的属性不是静态的,不能再静态代码块中赋值

package com.hspedu.final_;
​
public class FinalDetail {
}
​
class AA {
    //public final double TAX_RATE = 0.08;
//    public final double TAX_RATE2;
//
//    public AA() {
//        TAX_RATE2 = 6.6;
//    }
​
    public final double TAX_RATE3;
​
    {
        TAX_RATE3 = 8.8;
    }
}
​

3、如果final修饰的属性是静态的,则初始化的位置只能是

①定义时

②在静态代码块 不能在构造器中赋值

package com.hspedu.final_;
​
public class FinalDetail {
}
​
class AA {
    //public final double TAX_RATE = 0.08;
//    public final double TAX_RATE2;
//
//    public AA() {
//        TAX_RATE2 = 6.6;
//    }
​
    public final double TAX_RATE3;
​
    {
        TAX_RATE3 = 8.8;
    }
}
​
//3、如果final修饰的属性是静态的,则初始化的位置只能是
//①定义时
//②在静态代码块 不能在构造器中赋值
​
class CC {
    public static final double TAX_RATE;
​
    static {
        TAX_RATE = 9.9;
    }
}

4、final类中不能继承,但是可以实例化对象(也就是可以在main方法中创建对象)

package com.hspedu.final_;
​
public class FinalDetail {
    public static void main(String[] args) {
        //4、final类中不能继承,但是可以实例化对象(也就是可以在main方法中创建对象)
        BB bb = new BB();
    }
}
​
class AA {
    //public final double TAX_RATE = 0.08;
//    public final double TAX_RATE2;
//
//    public AA() {
//        TAX_RATE2 = 6.6;
//    }
​
    public final double TAX_RATE3;
​
    {
        TAX_RATE3 = 8.8;
    }
}
​
final class BB{
    
}
​
class CC {
    //3、如果final修饰的属性是静态的,则初始化的位置只能是
    //    ①定义时
    //    ②在静态代码块 不能在构造器中赋值
    public static final double TAX_RATE;
​
    static {
        TAX_RATE = 9.9;
    }
}

5、如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承

package com.hspedu.final_;
​
public class FinalDetail {
    public static void main(String[] args) {
        //5、如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
        DD dd = new DD();
        dd.Print();
    }
}
​
class AA {
    //public final double TAX_RATE = 0.08;
//    public final double TAX_RATE2;
//
//    public AA() {
//        TAX_RATE2 = 6.6;
//    }
​
    public final double TAX_RATE3;
​
    {
        TAX_RATE3 = 8.8;
    }
}
​
class CC {
    //3、如果final修饰的属性是静态的,则初始化的位置只能是
    //• ①定义时
    //• ②在静态代码块 不能在构造器中赋值
    public static final double TAX_RATE;
​
    static {
        TAX_RATE = 9.9;
    }
​
​
    //5、如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
    public final void Print() {
        System.out.println("CC类的Print方法");
    }
}
//5、如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
class DD extends CC{
​
}
​

9.5.3、final使用细节_2(P397)

6、一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。(类已经无法被继承,方法也就无法重写)

7、final不能修饰构造方法(即构造器)

8、final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化和处理

package com.hspedu.final_;
​
public class FinalDetail02 {
    public static void main(String[] args) {
        System.out.println(AAA.num);
    }
}
​
//8、final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化和处理
class AAA {
    public final static int num = 999;
​
    static {
        System.out.println("AAA类的静态代码块");
    }
​
    public AAA() {
        System.out.println("AAA类的无参构造器");
    }
}
​

输出结果为:

999
​

9、包装类(Integer,Double,Float,Boolean等都是final),String也是final类。

9.5.4、final课堂练习(P398)

1、请编写一个程序,能够计算原型的面积。要求圆周率为 3.14,赋值的位置3个方式都要写一下

package com.hspedu.final_;

//1、请编写一个程序,能够计算原型的面积。要求圆周率为 3.14,赋值的位置3个方式都要写一下
public class FinalExercise01 {
    public static void main(String[] args) {
        Circle circle = new Circle(2.0);
        System.out.println("第一种方法:" + circle.CircleArea());

        Circle circle1 = new Circle(3.0);
        System.out.println("第二种方法:" + circle1.CircleArea1());

        Circle circle2 = new Circle(6.0);
        System.out.println("第三种方法:" + circle2.CircleArea2());
    }
}

class Circle {
    private double radius;

    private final double PI = 3.14;//①
    private final double PI1;//②

    {
        PI1 = 3.14;
    }

    private final double PI2;

    public Circle(double radius) {
        PI2 = 3.14;
        this.radius = radius;
    }

    public double CircleArea() {
        return PI * radius * radius;
    }

    public double CircleArea1() {
        return PI1 * radius * radius;
    }

    public double CircleArea2() {
        return PI2 * radius * radius;
    }
}

输出结果为:

第一种方法:12.56
第二种方法:28.259999999999998
第三种方法:113.03999999999999
​

2、程序阅读题

public class Something {
    public int addOne (final int x) {//这里形参没有问题,调用方法时,一定会给x赋值,也就是赋初值
        ++x;//错误,final修饰后,不能修改值
        return x + 1;//正确,没有修改x的值
    }
}

9.6、抽象类(P398~402)

9.6.1、抽象类引出(P398)

  • 先看一个问题

class Animal {
    private String name;
    private int age;
​
    public Animal(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
​
    //动物都有eat的行为
    public void eat() {
        System.out.println("这是一个动物,但是目前不知到要吃什么");//子类调用还需要重写,很矛盾
    }
}

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

  • 抽象类快速入门

当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract来修饰该类就是抽象类。

package com.hspedu.abstract_;
​
public class Abstract01 {
    public static void main(String[] args) {
​
    }
}
​
abstract class Animal {
    private String name;
​
//    public void eat(){
//        System.out.println("不同动物吃不同的食物,没有实际用途");
//    }
    //思考:这里eat 你实现了这个方法,但是没有什么意义
    //即:父类方法不确定性的问题
    //===> 考虑将该方法设计为抽象(abstract)方法
    //===> 所谓抽象方法就是没有实现的方法
    //===> 所谓没有实现就是指,没有方法体
    //===> 当一个类中存在抽象方法时,需要将该类声明为abstract类
    //===> 一般来说,抽象类会被继承,由其子类来实现抽象方法
    abstract public void eat();
}
​

9.6.2、抽象类细节_1(P399)

  • 抽象类的介绍

1、用abstract关键字来修饰一个类时,这个类就叫抽象类

访问修饰符 abstract 类名{
​
}

2、用abstract关键字来修饰一个方法时,这个方法就是抽象方法

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

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

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

  • 抽象类使用的注意事项和细节讨论

1、抽象类不能被实例化

package com.hspedu.abstract_;
​
public class AbstractDetail01 {
    public static void main(String[] args) {
//    //①抽象类不能被实例化
//    new A();//这里会报错   
    }
}
abstract class A{
    
}
​

2、抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法,还可以有实现的方法

package com.hspedu.abstract_;
​
public class AbstractDetail01 {
    public static void main(String[] args) {
//    //1、抽象类不能被实例化
//    new A();//这里会报错
    }
}
​
abstract class A {
    //2、抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法,还可以有实现的方法
    public void hi() {
​
    }
}
​

3、一旦类包含了abstract方法,则这个类必须声明为abstract

4、abstract只能修饰类和方法,不能修饰属性和其它的。

9.6.2、抽象类细节_2(P400)

5、抽象类可以有任意成员[抽象类本质还是类],比如:非抽象方法(实现的方法)、构造器、静态属性等等

package com.hspedu.abstract_;
​
public class AbstractDetail02 {
    public static void main(String[] args) {
​
    }
}
//5、抽象类可以有任意成员[抽象类本质还是类],比如:非抽象方法(实现的方法)、构造器、静态属性等等
abstract class D {
    private int num = 999;
    private String name = "Mike";
​
    public void hi() {
        System.out.println("hi");
    }
​
    public static void hello() {
        System.out.println("777");
    }
​
    {
        System.out.println("123");
    }
​
    public D(int num, String name) {
        this.num = num;
        this.name = name;
    }
}
​

6、抽象方法不能有主体,即不能实现,如图所示

7、如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为抽象(abstract)类

package com.hspedu.abstract_;
​
public class AbstractDetail02 {
    public static void main(String[] args) {
​
    }
}
​
//5、抽象类可以有任意成员[抽象类本质还是类],比如:非抽象方法(实现的方法)、构造器、静态属性等等
abstract class D {
    private int num = 999;
    private String name = "Mike";
​
    public void hi() {
        System.out.println("hi");
    }
​
    public static void hello() {
        System.out.println("777");
    }
​
    {
        System.out.println("123");
    }
​
    public D(int num, String name) {
        this.num = num;
        this.name = name;
    }
}
​
//7、如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为抽象(abstract)类
abstract class E {
    public abstract void Print();
}
//①如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,
class G extends E{
    @Override
    public void Print() {
        
    }
}
//②除非它自己也声明为抽象(abstract)类
abstract class F extends E {
​
}

8、抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的。

//8、抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的。
abstract class H {
    abstract private void hell();//报错,去掉private正确
    
    abstract public final void hello();//报错,去掉final正确
​
    abstract public static void hello2();//报错去掉static正确
}
​

9.6.3、抽象类课堂练习(P401)

1、abstract final class A{} 编译能通过吗

不能,abstract不能与final在一起使用,final不能继承

2、abstract public static void test2(); 编译能通过吗

不能,abstract不能与static 在一起使用,test2无法被重写

3、abstract private void test3();编译能通过吗

不能,abstract不能与private 在一起使用,private 私有方法无法被重写

4、编写一个Employee类,声明为抽象类,包含如下三个属性:name,id,salary。提供必要的构造器和抽象方法:work()。对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问,实现work(),提示"经理/普通员工 名字 工作中"

main方法:

package com.hspedu.abstract_;
​
//4、编写一个Employee类,声明为抽象类,包含如下三个属性:name,id,salary。
// 提供必要的构造器和抽象方法:work()。
// 对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。
// 请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问,
// 实现work(),提示"经理/普通员工 名字 工作中"
public class AbstractExercise01 {
    public static void main(String[] args) {
        CommonEmployee mike = new CommonEmployee("Mike", 1, 3000);
        mike.work();
        
        Manager john = new Manager("John", 2, 5000);
        john.setBonus(1000);
        john.work();
    }
}
​

父类Employee:

package com.hspedu.abstract_;
​
//编写一个Employee类,声明为抽象类,包含如下三个属性:name,id,salary。
abstract public class Employee {
    private String name;
    private int id;
    private double salary;
​
    提供必要的构造器和抽象方法:work()。
    public Employee(String name, int id, double salary) {
        this.name = name;
        this.id = id;
        this.salary = salary;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getId() {
        return id;
    }
​
    public void setId(int id) {
        this.id = id;
    }
​
    public double getSalary() {
        return salary;
    }
​
    public void setSalary(double salary) {
        this.salary = salary;
    }
​
    public abstract void work();
}

子类Manager:

package com.hspedu.abstract_;
​
// 对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。
// 请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问,
// 实现work(),提示"经理/普通员工 名字 工作中"
public class Manager extends Employee {
    private double bonus;
​
    public Manager(String name, int id, double salary) {
        super(name, id, salary);
    }
​
    @Override
    public void work() {
        System.out.println("经理" + getName() + "工作中");
    }
​
    public double getBonus() {
        return bonus;
    }
​
    public void setBonus(double bonus) {
        this.bonus = bonus;
    }
}

子类CommonEmployee:

package com.hspedu.abstract_;
​
// 请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问,
// 实现work(),提示"经理/普通员工 名字 工作中"
public class CommonEmployee extends Employee {
    public CommonEmployee(String name, int id, double salary) {
        super(name, id, salary);
    }
​
    @Override
    public void work() {
        System.out.println("普通员工" + super.getName() + "工作中");
    }
}

9.6.4、抽象模板模式(P402)

  • 最佳实践

需求

①有多个类,完成不同的任务job

②要求统计得到各自完成任务的时间

③编程实现

main方法:

package com.hspedu.abstract_;
​
public class TestTemplate {
    public static void main(String[] args) {
        AA aa = new AA();
        aa.calculate();
​
        BB bb = new BB();
        bb.calculate();
    }
}

父类Template:

package com.hspedu.abstract_;
​
public abstract class Template {//抽象类-模板设计模式
    public abstract void job();//抽象方法
​
    public void calculate() {//实现方法,调用job方法
        //得到开始时间
        long start = System.currentTimeMillis();
        job();//动态绑定机制
        //得到结束时间
        long end = System.currentTimeMillis();
        System.out.println("任务执行时间:" + (end - start));
    }
}

子类AA:

package com.hspedu.abstract_;
​
public class AA extends Template {
    @Override
    public void job() {
        long sum = 0;
        for (int i = 0; i < 80000; i++) {
            sum *= i;
        }
    }
}

子类BB:

package com.hspedu.abstract_;
​
public class BB extends Template{
    @Override
    public void job() {
        long sum = 0;
        for (int i = 0; i < 800000; i++) {
            sum *= i;
        }
    }
}

输出结果为:

任务执行时间:1
任务执行时间:3

9.7、接口(P403~412)

9.7.1、接口快速入门(P403)

  • 为什么有接口

先看一张图

  • 接口快速入门

这样的设计需求在java编程/php/.net/go中也是会大量存在的,一个程序就是一个世界,在现实世界存在的情况,在程序中也会出现。

main方法:

package com.hspedu.interface_;
​
public class Interface01 {
    public static void main(String[] args) {
        //创建手机和相机对象
        Phone phone = new Phone();
        Camera camera = new Camera();
​
        //创建计算机
        Computer computer = new Computer();
        computer.work(phone);//把手机接入电脑
        System.out.println("==============================");//分割线
        computer.work(camera);
    }
}

接口UsbInterface:

package com.hspedu.interface_;
​
public interface UsbInterface {//接口
​    //接口里面抽象方法可以不写abstract
    //规定接口的相关方法
    public void start();
​
    public void end();
}

Computer类:

package com.hspedu.interface_;
​
public class Computer {
    //编写一个方法
    public void work(UsbInterface usbInterface) {
        //通过接口来调用方法
        usbInterface.start();
        usbInterface.end();
    }
}

Camera类:

package com.hspedu.interface_;
​
public class Camera implements UsbInterface{
    @Override
    public void start() {
        System.out.println("相机开始传输数据...");
    }
​
    @Override
    public void end() {
        System.out.println("相机停止传输数据...");
    }
}

Phone类:

package com.hspedu.interface_;

//Phone类实现UsbInterface
//1、Phone类需要实现UsbInterface接口 规定/声明的方法
public class Phone implements UsbInterface{
    @Override
    public void start() {
        System.out.println("手机开始传输数据...");
    }

    @Override
    public void end() {
        System.out.println("手机停止传输数据...");
    }
}

输出结果为:

手机开始传输数据...
手机停止传输数据...
==============================
相机开始传输数据...
相机停止传输数据...
​

9.7.2、接口基本介绍(P404)

  • 基本介绍

接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。

语法:

interface 接口名{
    //属性
    //方法
}
class 类名 implements 接口 {
    自己属性;
    自己方法;
    必须实现的接口的抽象方法
}

小结:

1、接口中的方法全部是抽象方法,默认public abstract 修饰,也只能是public abstract修饰,public abstract可以省略

2、接口中的方法有抽象方法(abstract),默认方法(default),静态方法(static)接口中所有方法的访问权限修饰符都是public,也只能是public,public 可以省略

main方法:

package com.hspedu.interface_;
​
public class Interface02 {
    public static void main(String[] args) {
​
    }
}
​
class AAA implements AInterface {
    //必须实现的接口的抽象方法
​
    @Override
    public void hi() {
​
    }
​
    @Override
    public void hi2() {
​
    }
​
    @Override
    public void hello() {
​
    }
}

AInterface接口:

package com.hspedu.interface_;
​
public interface AInterface {
    //属性
    public int num = 0;
​
    //方法
    //在接口中,抽象方法,可以省略abstract关键字
    public void hello();
​
    //从jdk8开始后,可以有默认和public实现方法,需要使用default关键字修饰
    default void hi() {
​
    }
​
    default public void hi2() {
​
    }
​
    //从jdk8开始后,可以有静态方法,静态方法不需要重写
    public static void staticHi() {
​
    }
}

9.7.3、接口应用场景(P405)

  • 深入讨论

对于初学者来讲,理解接口的概念不算太难,难的是不知道什么时候使用接口

应用场景:接口规定方法名称

代码:

main方法:

package com.hspedu.interface_;
​
public class Interface03 {
    public static void main(String[] args) {
        MySQL mySQL = new MySQL();
        call(mySQL);
​
        System.out.println("=======================");//分割线
        Oracle oracle = new Oracle();
        call(oracle);
    }
​
    public static void call(DBInterface db) {
        db.connect();
        db.close();
    }
}

接口DBInterface:

package com.hspedu.interface_;
​
public interface DBInterface {
    public void connect();
​
    public void close();
}

MySQL类(程序员A写):

package com.hspedu.interface_;
//A程序员写数据库连接。使用接口,这样方法命名就不可更改了
public class MySQL implements DBInterface {
    @Override
    public void connect() {
        System.out.println("连接数据库");
    }
​
    @Override
    public void close() {
        System.out.println("关闭数据库");
    }
}

Oracle类(程序员B写):

package com.hspedu.interface_;
​
public class Oracle implements DBInterface {
    @Override
    public void connect() {
        System.out.println("连接Oracle");
    }
​
    @Override
    public void close() {
        System.out.println("关闭Oracle");
    }
}

9.7.4、接口使用细节_1(P406)

  • 注意事项

1、接口不能被实例化

package com.hspedu.interface_;
​
public class InterfaceDetail01 {
    public static void main(String[] args) {
//        //1、接口不能被实例化
//        new IA();//这里会报错
    }
}
interface IA{}
​

2、接口中所有的方法是public方法;接口中抽象方法,可以不用abstract修饰

package com.hspedu.interface_;
​
public class InterfaceDetail01 {
    public static void main(String[] args) {
//        //1、接口不能被实例化
//        new IA();//这里会报错
    }
}
​
interface IA {
    //2、接口中所有的方法是public方法;接口中抽象方法,可以不用abstract修饰
    public abstract void hello1();
​
    public void hello2();
}

3、一个普通类实现接口,就必须将该接口的所有方法都实现

package com.hspedu.interface_;
​
public class InterfaceDetail01 {
    public static void main(String[] args) {
//        //1、接口不能被实例化
//        new IA();//这里会报错
    }
}
​
interface IA {
    //2、接口中所有的方法是public方法;接口中抽象方法,可以不用abstract修饰
    public abstract void hello1();
​
    public void hello2();
}
//3、一个普通类实现接口,就必须将该接口的所有方法都实现。
class Cat implements IA{
    @Override
    public void hello1() {
        
    }
​
    @Override
    public void hello2() {
​
    }//在接口名前,摁下alt+enter快捷键,创建方法
​
}

4、抽象类实现接口,可以不用实现接口的方法

package com.hspedu.interface_;
​
public class InterfaceDetail01 {
    public static void main(String[] args) {
//        //1、接口不能被实例化
//        new IA();//这里会报错
    }
}
​
interface IA {
    //2、接口中所有的方法是public方法;接口中抽象方法,可以不用abstract修饰
    public abstract void hello1();
​
    public void hello2();
}
//3、一个普通类实现接口,就必须将该接口的所有方法都实现。
class Cat implements IA{
    @Override
    public void hello1() {
        
    }
​
    @Override
    public void hello2() {
​
    }//在接口名前,摁下alt+enter快捷键,创建方法
​
}
//4、抽象类实现接口,可以不用实现接口的方法
abstract class Tiger implements IA{}

9.7.5、接口使用细节_2(P407)

5、一个类可以同时实现多个接口

package com.hspedu.interface_;
​
public class InterfaceDetail02 {
    public static void main(String[] args) {
​
    }
}
​
interface IB {
    void Pig();
}
​
interface IC {
    void Cat();
}
//5、一个类可以同时实现多个接口
class Tools implements IB,IC{
​
    @Override
    public void Pig() {
        
    }
​
    @Override
    public void Cat() {
​
    }
}

6、接口中的属性,只能是final的,而且是public static final修饰符。比如:int a = 1;实际上是public static final int a = 1;(必须初始化)

package com.hspedu.interface_;

public class InterfaceDetail02 {
    public static void main(String[] args) {
        System.out.println(IB.a);//能使用 类名.属性名 调用,证明属性被static修饰
//        IB.a = 10;//这里会报错,也就说明变量a被final修饰
    }
}

interface IB {
    //6、接口中的属性,只能是final的,而且是public static final修饰符。
    // 比如:int a = 1;实际上是public static final int a = 1;(必须初始化)
    int a = 1;

    void Pig();
}

interface IC {
    void Cat();
}

//5、一个类可以同时实现多个接口
class Tools implements IB, IC {

    @Override
    public void Pig() {

    }

    @Override
    public void Cat() {

    }
}

7、接口中的属性的访问形式:接口名.属性名

8、接口中不能继承其它的类,但是可以继承多个别的接口

package com.hspedu.interface_;
​
public class InterfaceDetail02 {
    public static void main(String[] args) {
        System.out.println(IB.a);//能使用 类名.属性名 调用,证明属性被static修饰
//        IB.a = 10;//这里会报错,也就说明变量a被final修饰
    }
}
​
interface IB {
    //6、接口中的属性,只能是final的,而且是public static final修饰符。
    // 比如:int a = 1;实际上是public static final int a = 1;(必须初始化)
    int a = 1;
​
    void Pig();
}
​
interface IC {
    void Cat();
}
​
//8、接口中不能继承其它的类,但是可以继承多个别的接口
interface ID extends IB, IC {
    
}
​
//5、一个类可以同时实现多个接口
class Tools implements IB, IC {
​
    @Override
    public void Pig() {
​
    }
​
    @Override
    public void Cat() {
​
    }
}

9、接口的修饰符只能是public和默认,这点和类的修饰符是一样的。

9.7.6、接口的课堂练习(P408)

看下面代码是否正确?如果正确,输出什么?

package com.hspedu.interface_;
​
public class InterfaceExercise01 {
    public static void main(String[] args) {
        B b = new B();
        //B类实现了AA接口,所以可以使用属性a
        System.out.println(b.a);
        System.out.println(B.a);
        System.out.println(AA.a);
    }
}
​
interface AA {
    int a = 23;//等价于public static final int a = 23;
}
​
class B implements AA {
​
}

输出结果为:

23
23
23
​

9.7.7、接口VS继承(P409)

package com.hspedu.interface_;

public class Extends_VS_Interface {
    public static void main(String[] args) {
        littleMonkey wuKong = new littleMonkey("悟空");
        wuKong.clime();
        wuKong.fly();
        wuKong.swim();
    }
}

interface fishAble {
    void swim();
}

interface birdAble {
    void fly();
}

class Monkey {
    private String name;

    public void clime() {
        System.out.println("猴子会爬树...");
    }

    public Monkey(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
//小结,当子类继承了父类,就自动拥有了父类的功能
//  如果子类需要扩展功能,可以通过实现接口的方式扩展
//  可以理解 实现接口 是 对java单继承机制的一种补充
class littleMonkey extends Monkey implements fishAble, birdAble {

    public littleMonkey(String name) {
        super(name);
    }

    @Override
    public void swim() {
        System.out.println(getName() + "通过学习,会游泳");
    }

    @Override
    public void fly() {
        System.out.println(getName() + "通过学习,会飞翔");
    }
}

输出结果为:

猴子会爬树...
悟空通过学习,会飞翔
悟空通过学习,会游泳
​

小结:

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

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

接口的价值主要在于:设计好各种规范(方法),让其它类去实现这些方法。即更加灵活。

2、接口比继承更加灵活

继承是满足 is - a的关系,而接口只需满足 like - a的关系

3、接口在一定程序上实现代码解耦[即:接口规范性+动态绑定机制]

9.7.8、接口多态特性(P410)

  • 接口的多态特性

1、多态参数

在前面的Usb接口案例,UsbInterface usbInterface ,既可以接收手机对象,又可以接收相机对象,就体现了 接口 多态(接口引用可以指向实现了接口的类的对象)

package com.hspedu.interface_;
​
public class InterfacePolyParameter {
    public static void main(String[] args) {
//接口多态的体现
        //接口类型的变量 a1 可以指向 实现了接口A1的类的对象实例
        A1 a1 = new N1();
        a1 = new N2();
//继承多态的体现
        //父类类型变量q,可以指向继承了Q的子类的实例对象
        Q q = new Q1();
        q = new Q2();
    }
}
​
// 1
interface A1 {
}
​
class N1 implements A1 {
}
​
class N2 implements A1 {
}
​
//2
class Q {
​
}
​
class Q1 extends Q {
}
​
class Q2 extends Q {
}

2、多态数组

演示一个案例:给Usb数组中,存放Phone 和相机对象,Phone类还有一个特有的方法call(),请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,还需要调用Phone特有方法call

package com.hspedu.interface_;
​
public class InterfacePolyArr {
    //演示一个案例:
    // 给Usb数组中,存放Phone 和相机对象,Phone类还有一个特有的方法call(),
    // 请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,还需要调用Phone特有方法call
    public static void main(String[] args) {
        Usb[] usb = new Usb[2];
        usb[0] = new Phone_();
        usb[1] = new Camera_();
        for (int i = 0; i < usb.length; i++) {
            usb[i].work();//动态绑定
            if (usb[i] instanceof Phone_) {//判断类似是否为Phone类型
                ((Phone_) usb[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("相机正在工作中");
    }
}

输出结果为:

手机正在工作中
手机正在打电话//
相机正在工作中
​

9.7.9、接口多态传递(继承)(P411)

(接上一节)3、接口存在传递现象

package com.hspedu.interface_;
​
public class InterfacePolyPass {
    public static void main(String[] args) {
        //接口类型的变量可以指向,实现了该接口的类的对象实例
        IG ig = new T();
        //这就是多态传递现象
        IH ih = new T();//IG接口继承了IH,所以ih也可以指向实现IG的类
    }
}
​
interface IH {
    void hi();
}
​
interface IG extends IH {
}
​
class T implements IG {
​
    @Override
    public void hi() {
        
    }
}

9.7.10、接口课堂练习(P412)

看下面代码有没有错误,有错误就修改,改好后,输出什么。

package com.hspedu.interface_;
​
public class InterfaceExercise02 {
    public static void main(String[] args) {
        C_ c_ = new C_();
        c_.Px();
    }
}
​
interface A {
    int x = 0;
}
​
class B_ {
    int x = 1;
}
​
class C_ extends B_ implements A {
    public void Px() {
        System.out.println(x);
    }
}

修改后:

package com.hspedu.interface_;
​
public class InterfaceExercise02 {
    public static void main(String[] args) {
        C_ c_ = new C_();
        c_.Px();
    }
}
​
interface A {
    int x = 0;
}
​
class B_ {
    int x = 1;
}
​
class C_ extends B_ implements A {
    public void Px() {
        System.out.println("接口A的x:" + A.x + "\nB类中的x:" + super.x);
    }
}

输出结果为:

接口A的x:0
B类中的x:1
​

 

(5)内部类  

9.8、内部类(P413~423)

9.8.1、四种内部类(P413)

  • 基本介绍

一个类的内部又完整的嵌套类另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员[类的五大成员为:属性、方法、构造器、代码块、内部类],内部类的最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类。

  • 基本语法

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

  • 快速入门

package com.hspedu.innerclass;
​
public class InnerClass01 {//外部其他类
    public static void main(String[] args) {
​
    }
}
​
class Outer {//外部类
    private int num = 999;//属性
​
    public Outer(int num) {//构造器
        this.num = num;
    }
​
    public void m1() {//方法
        System.out.println("m1()");
    }
​
    {//代码块
        System.out.println("代码块");
    }
​
    class InnerClass {//内部类
        
    }
}

  • 内部类的分类

1、定义在外部类局部位置上(比如方法内):

①局部内部类(有类名)

②匿名内部类(没有类名,重点!!!)

2、定义在外部类的成员位置上:

①成员内部类(没有static修饰)

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

9.8.2、局部内部类_1(P414)

  • 局部内部类的使用

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

package com.hspedu.innerclass;
​
public class InnerClass02 {
    public static void main(String[] args) {
​
    }
}
​
class Outer02 {
    private int n1 = 100;
​
    public void m1() {//方法
        //说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
        class Inner02 {//局部内部类
            
        }
    }
}

1、可以直接访问外部类的所有成员,包含私有的

package com.hspedu.innerclass;
​
public class InnerClass02 {
    public static void main(String[] args) {
​
    }
}
​
class Outer02 {//外部类
    private int n1 = 100;
    private void m2(){};
​
    public void m1() {//方法
        //说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
        class Inner02 {//局部内部类
            //1、可以直接访问外部类的所有成员,包含私有的
            public void f1(){
                System.out.println("n1="+n1);//访问外部类私有属性
                m2();//访问外部类私有方法
            }
        }
    }
}

2、内部类不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final

package com.hspedu.innerclass;
​
public class InnerClass02 {
    public static void main(String[] args) {
​
    }
}
​
class Outer02 {//外部类
    private int n1 = 100;
    private void m2(){};
​
    public void m1() {//方法
        //说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
        //2、内部类不能添加访问修饰符,因为它的地位就是一个局部变量。
        // 局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
        final class Inner02 {//局部内部类
            //1、可以直接访问外部类的所有成员,包含私有的
            public void f1(){
                System.out.println("n1="+n1);//访问外部类私有属性
                m2();//访问外部类私有方法
            }
        }
        //class NN extends Inner02{}//继承的父类添加了final,所以这里会报错
    }
}

3、作用域:仅仅在定义它的方法代码块中。

4、局部内部类---访问----->外部类的成员[访问方式:直接访问]

package com.hspedu.innerclass;
​
public class InnerClass02 {
    public static void main(String[] args) {
​
    }
}
​
class Outer02 {//外部类
    private int n1 = 100;
    private void m2(){};
​
    public void m1() {//方法
        //说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
        //2、内部类不能添加访问修饰符,因为它的地位就是一个局部变量。
        // 局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
        final class Inner02 {//局部内部类(本质仍然是一个类)
            //1、可以直接访问外部类的所有成员,包含私有的
            public void f1(){
                //4、局部内部类---访问----->外部类的成员[访问方式:直接访问],如下所示
                System.out.println("n1="+n1);//访问外部类私有属性
                m2();//访问外部类私有方法
            }
        }
        //class NN extends Inner02{}//继承的父类添加了final,所以这里会报错
    }
}

5、外部类---访问----->局部内部类的成员

访问方式:创建对象,再访问(注意:必须在作用域内)

package com.hspedu.innerclass;
​
public class InnerClass02 {
    public static void main(String[] args) {
​
    }
}
​
class Outer02 {//外部类
    private int n1 = 100;
    private void m2(){};
​
    public void m1() {//方法
        //说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
        //2、内部类不能添加访问修饰符,因为它的地位就是一个局部变量。
        // 局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
        final class Inner02 {//局部内部类(本质仍然是一个类)
            //1、可以直接访问外部类的所有成员,包含私有的
            public void f1(){
                //4、局部内部类---访问----->外部类的成员[访问方式:直接访问],如下所示
                System.out.println("n1="+n1);//访问外部类私有属性
                m2();//访问外部类私有方法
            }
        }
        //class NN extends Inner02{}//如果继承的父类添加了final,所以这里会报错
​
        //5、外部类---访问----->局部内部类的成员
        //访问方式:创建对象,再访问(注意:必须在作用域内)
        //也就是说,在外部类方法中,可以创建Inner02对象,然后调用方法即可
        Inner02 inner02 = new Inner02();
        inner02.f1();
    }
}

记住:

1、局部内部类定义在方法/代码块

2、作用域在方法体或者代码块中

3、本质仍然是一个类

package com.hspedu.innerclass;
​
public class InnerClass02 {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        outer02.m1();
    }
}
​
class Outer02 {//外部类
    private int n1 = 100;
​
    private void m2() {
        System.out.println("Outer02的m2方法被调用");
    }
​
    public void m1() {//方法
        //说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
        //2、内部类不能添加访问修饰符,因为它的地位就是一个局部变量。
        // 局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
        final class Inner02 {//局部内部类(本质仍然是一个类)
​
            //1、可以直接访问外部类的所有成员,包含私有的
            public void f1() {
                //4、局部内部类---访问----->外部类的成员[访问方式:直接访问],如下所示
                System.out.println("n1=" + n1);//访问外部类私有属性
                m2();//访问外部类私有方法
            }
        }
        //class NN extends Inner02{}//如果继承的父类添加了final,所以这里会报错
​
        //5、外部类---访问----->局部内部类的成员
        //访问方式:创建对象,再访问(注意:必须在作用域内)
        //也就是说,在外部类方法中,可以创建Inner02对象,然后调用方法即可
        Inner02 inner02 = new Inner02();
        System.out.println("是不是先走这里,再去调用内部类中的f1方法");
        inner02.f1();
    }
}

输出结果为:

是不是先走这里,再去调用内部类中的f1方法
n1=100
Outer02的m2方法被调用
​

9.8.3、局部内部类_2(P415)

6、外部其他类---不能访问----->局部内部类(因为 局部内部类地位是一个局部变量)

7、如果外部类和局部内部类的成员重名时,默认遵循就近原则。如果想访问外部类的成员,则可以使用 外部类名.this.成员 去访问

package com.hspedu.innerclass;
​
public class InnerClass02 {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        outer02.m1();
        //7.2、Outer02.this本质就是外部类的对象,即哪个对象调用了m1,Outer02.this就是哪个对象
        System.out.println("outer02的hashCode:" + outer02.hashCode());
    }
}
​
class Outer02 {//外部类
    private int n1 = 100;
​
    private void m2() {
        System.out.println("Outer02的m2方法被调用");
    }
​
    public void m1() {//方法
        //说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
        //2、内部类不能添加访问修饰符,因为它的地位就是一个局部变量。
        // 局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
        final class Inner02 {//局部内部类(本质仍然是一个类)
            private int n1 = 999;
​
            //1、可以直接访问外部类的所有成员,包含私有的
            public void f1() {
                //4、局部内部类---访问----->外部类的成员[访问方式:直接访问],如下所示
                System.out.println("局部内部类n1=" + n1);//访问外部类私有属性
                m2();//访问外部类私有方法
                //7.1、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员 去访问。
                System.out.println("外部类n1=" + Outer02.this.n1);
                //7.2、Outer02.this本质就是外部类的对象,即哪个对象调用了m1,Outer02.this就是哪个对象
                System.out.println("Outer02.this的hashCode:" + Outer02.this.hashCode());
            }
        }
        //class NN extends Inner02{}//如果继承的父类添加了final,所以这里会报错
​
        //5、外部类---访问----->局部内部类的成员
        //访问方式:创建对象,再访问(注意:必须在作用域内)
        //也就是说,在外部类方法中,可以创建Inner02对象,然后调用方法即可
        Inner02 inner02 = new Inner02();
        System.out.println("是不是先走这里,再去调用内部类中的f1方法");
        inner02.f1();
    }
}

输出结果为:

是不是先走这里,再去调用内部类中的f1方法
局部内部类n1=999
Outer02的m2方法被调用
外部类n1=100
Outer02.this的hashCode:460141958
outer02的hashCode:460141958
​

9.8.4、匿名内部类本质(P416)

  • 匿名内部类的使用(重要!!!)

说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名(系统会自定义类名,只是看不到)。本质上还是一个类(内部类),同时还是一个对象

1、匿名内部类的基本语法

new 类或接口(参数列表){
    类体
};

代码演示:

package com.hspedu.innerclass;
​
public class AnonymousInnerClass {
    public static void main(String[] args) {
        T t = new T();
        t.tiger();
    }
}
​
interface IA {
    void cry();
}
​
class T {
    public void tiger() {
        //创建匿名内部类
        //在jdk底层创建匿名内部类T$1,后面立即创建了T$1的实例,并吧地址返回给ia
        //匿名内部类使用一次,就不能再次使用
        IA ia = new IA() {//编译类型为IA,运行类型为匿名内部类T$1
            @Override
            public void cry() {
                System.out.println("老虎在叫唤...");
            }
        };
        ia.cry();
        System.out.println("ia的运行类型" + ia.getClass());//看运行结果,最后面就是系统给匿名内部类定义的类名
    }
}

输出结果为:

老虎在叫唤...
ia的运行类型class com.hspedu.innerclass.T$1
​

9.8.5、匿名内部类的使用(P417)

package com.hspedu.innerclass;
​
public class AnonymousInnerClass {
    public static void main(String[] args) {
        T t = new T();
        t.tiger();
    }
}
​
interface IA {
    void cry();
}
​
class T {
    public void tiger() {
        //创建匿名内部类
        //在jdk底层创建匿名内部类T$1,后面立即创建了T$1的实例,并吧地址返回给ia
        //匿名内部类使用一次,就不能再次使用
        IA ia = new IA() {//编译类型为IA,运行类型为匿名内部类T$1
            @Override
            public void cry() {
                System.out.println("老虎在叫唤...");
            }
        };
        ia.cry();
        System.out.println("ia的运行类型" + ia.getClass());//看运行结果,最后面就是系统给匿名内部类定义的类名
​
        //普通类的匿名内部类,不强制实现普通类中的方法
        Father father = new Father("jack") {//参数列表中的jack,传入Father中的构造器
            @Override
            public void test() {
                System.out.println("测试方法");
            }
        };
        System.out.println("father的运行类型:" + father.getClass());
        father.test();
​
        //基于抽象类的匿名内部类,强制实现抽象类中的抽象方法
        Animal animal = new Animal() {
            @Override
            void eat() {
                System.out.println("动物吃");
            }
        };
        System.out.println("animal的运行类型" + animal.getClass());
        animal.eat();
    }
}
​
class Father {
    private String name;
​
    public Father(String name) {//构造器
        this.name = name;
        System.out.println("接收到了" + name);
    }
​
    public void test() {//方法
    }
}
​
abstract class Animal {
    abstract void eat();
}

输出结果为:

老虎在叫唤...
ia的运行类型class com.hspedu.innerclass.T$1
接收到了jack
father的运行类型:class com.hspedu.innerclass.T$2
测试方法
animal的运行类型class com.hspedu.innerclass.T$3
动物吃
​

9.8.6、匿名内部类细节(P418)

(接P416)

2、匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征。调用匿名内部类方法有两种

package com.hspedu.innerclass;
​
public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.f1();
    }
}
​
class Outer05 {
    public void f1() {
        //创建一个基于类的匿名内部类
        //底层还是 class 匿名内部类 extends Person {}
        Person person = new Person() {
            @Override
            public void hi() {
                System.out.println("匿名内部类中的hi()");
            }
        };
        person.hi();//第一种调用方法,动态绑定,运行类型是Outer05$1
​
        new Person(){
            @Override
            public void hi() {
                super.hi();
            }
        }.hi();//第二种调用方法,可以直接调用,匿名内部类本身也是返回对象
​
        new Person(){
            @Override
            public void ok(String str) {
                super.ok(str);
            }
        }.ok("匿名内部类调用ok方法");
    }
}
​
class Person {
    public void hi() {
        System.out.println("Person hi()");
    }
​
    public void ok(String str) {
        System.out.println("Person ok()" + str);
    }
}

输出结果为:

匿名内部类中的hi()
Person hi()
Person ok()匿名内部类调用ok方法
​

3、可以直接访问外部类的所有成员,包含私有的

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

5、作用域:仅仅在定义它的方法或代码块中

6、匿名内部类---访问----->外部类成员[访问方式:直接访问]

7、外部其他类---不能访问----->匿名内部类。[匿名内部类本身就是一个局部变量]

8、如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

9.8.7、匿名内部类实践(P419)

  • 匿名内部类的最佳实践

匿名内部类,当作实参直接传递,简洁高效。

package com.hspedu.innerclass;
​
public class InnerClassExercise01 {
    public static void main(String[] args) {
​
        //1、匿名内部类,当作实参直接传递,简洁高效。//适合单次使用
        f1(new IA1() {
            //直接传参不会影响实例,
            @Override
            public void show() {
                System.out.println("这是一幅名画...");
            }
        });
​
        //2、传统方法----->类实现IL1-----》编程领域(硬编码)-----》适合多次使用
        f1(new Picture());
    }
​
    public static void f1(IA1 ia1) {
        ia1.show();
    }
}
​
interface IA1 {
    void show();
}
//2、传统方法----->类实现IL1-----》编程领域(硬编码)-----》适合多次使用
class Picture implements IA1{
    //在类里修改内容,会影响所以Picture实例
    @Override
    public void show() {
        System.out.println("这是一幅名画...");
    }
}

输出结果为:

这是一幅名画...
这是一幅名画...
​

  • 匿名内部类课堂练习

1、有一个铃声接口Bell,里面有个ring方法。

2、有一个手机类cellPhone,具有闹钟功能alarmClock,参数是Bell类型

3、测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了

4、再传入另一个匿名内部类(对象),打印:小伙伴上课了

package com.hspedu.innerclass;
​
public class InnerClassExercise02 {
    public static void main(String[] args) {
        //1、有一个铃声接口Bell,里面有个ring方法。
        //2、有一个手机类cellPhone,具有闹钟功能alarmClock,参数是Bell类型
        //3、测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
        //4、再传入另一个匿名内部类(对象),打印:小伙伴上课了
        
        //下面重写了ring
        //Bell bell = new Bell() {
        //            @Override
        //            public void ring() {
        //                System.out.println("懒猪起床了");
        //            }
        //        }
        cellPhone.alarmClock(new Bell() {//cellPhone.alarmClock传入的参数(匿名内部类)需要实现接口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 static void alarmClock(Bell bell) {//编译类型是Bell,运行类型是变化的
        bell.ring();//动态绑定机制
    }
}

输出结果为:

懒猪起床了
小伙伴上课了
​

9.8.8、成员内部类_1(P420)

  • 成员内部类的使用

说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。

1、可以直接访问外部类的所有成员,包含私有的。

package com.hspedu.innerclass;
​
public class MemberInnerClass {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.call();
    }
}
​
class Outer08 {
    private int n1 = 10;
    public String name = "张三";
​
    //成员内部类定义在外部类的的成员位置上
    class Inner08 {
        public void say() {
            //1、可以直接访问外部类的所有成员,包含私有的。
            System.out.println("n1 = " + n1 + "\nname = " + name);
        }
    }
    public void call(){
        Inner08 inner08 = new Inner08();
        inner08.say();
    }
}

输出结果为:

n1 = 10
name = 张三
​

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

package com.hspedu.innerclass;
​
public class MemberInnerClass {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.call();
    }
}
​
class Outer08 {
    private int n1 = 10;
    public String name = "张三";
​
    //成员内部类定义在外部类的的成员位置上
    //2、可以添加任意访问修饰符(public、protected、默认、private),因为**它的地位就是一个成员**。
    private class Inner08 {
        public void say() {
            //1、可以直接访问外部类的所有成员,包含私有的。
            System.out.println("n1 = " + n1 + "\nname = " + name);
        }
    }
​
    public class Inner081 {
    }
​
    protected class Inner082 {
    }
​
    public void call() {
        Inner08 inner08 = new Inner08();
        inner08.say();
    }
}

输出结果为:

n1 = 10
name = 张三
​

9.8.9、成员内部类_2(P421)

3、作用域:和外部类的其他成员一样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法。

4、成员内部类---访问----->外部类(比如:属性)[访问方式:直接访问]

package com.hspedu.innerclass;
​
public class MemberInnerClass {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.call();
    }
}
​
class Outer08 {
    private int n1 = 10;
    public String name = "张三";
​
    private void f1() {
        System.out.println("f1()方法被调用...");
    }
​
    //成员内部类定义在外部类的的成员位置上
    //2、可以添加任意访问修饰符(public、protected、默认、private),因为**它的地位就是一个成员**。
    private class Inner08 {
        public void say() {
            //1、可以直接访问外部类的所有成员,包含私有的。
            System.out.println("n1 = " + n1 + "\nname = " + name);
            //4、成员内部类---访问----->外部类(比如:属性)[访问方式:直接访问]
            f1();
        }
    }
​
    public class Inner081 {
    }
​
    protected class Inner082 {
    }
​
    public void call() {
        Inner08 inner08 = new Inner08();
        inner08.say();
    }
}

输出结果为:

n1 = 10
name = 张三
f1()方法被调用...
​

5、外部类---访问----->内部类 [访问方式:创建对象,再访问] //上面代码call方法调用成员内部类的say方法

6、外部其他类---访问---成员内部类

package com.hspedu.innerclass;
​
public class MemberInnerClass {
    public static void main(String[] args) {
        //6、外部其他类---访问---成员内部类
​
        //方式1
        //outer08.new Inner08(); 相当于把 new Inner08(); 当作outer08的成员
        Outer08 outer08 = new Outer08();
        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();
​
        System.out.println("========================");//分割线
​
        //方式2
        //在外部类中,编写一个方法,返回Inner08对象
        Outer08.Inner08 inner081 = outer08.getInner08();
        inner081.say();
    }
}
​
class Outer08 {
    private int n1 = 10;
    public String name = "张三";
​
    private void f1() {
        System.out.println("f1()方法被调用...");
    }
​
    //成员内部类定义在外部类的的成员位置上
    //2、可以添加任意访问修饰符(public、protected、默认、private),因为**它的地位就是一个成员**。
    public class Inner08 {
        public void say() {
            //1、可以直接访问外部类的所有成员,包含私有的。
            System.out.println("n1 = " + n1 + "\nname = " + name);
            //4、成员内部类---访问----->外部类(比如:属性)[访问方式:直接访问]
            f1();
        }
    }
​
    public class Inner081 {
    }
​
    protected class Inner082 {
    }
​
    public Inner08 getInner08() {
        return new Inner08();
    }
​
    public void call() {
        Inner08 inner08 = new Inner08();
        inner08.say();
    }
}

输出结果为:

n1 = 10
name = 张三
f1()方法被调用...
========================
n1 = 10
name = 张三
f1()方法被调用...
​

7、如果外部类和内部类的成员重名是,内部类访问的话,默认遵循就近原则。如果想访问外部类的成员,则可以使用 外部类名.this.成员 去访问

9.8.10、静态内部类_1(P422)

  • 静态内部类的使用

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

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

package com.hspedu.innerclass;
​
public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
​
​
    }
}
​
class Outer10 {
    private int n1 = 10;
    public String name1 = "Mike";
    private static String name2 = "Jack";
​
    //1、可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员。
    static class Inner10 {
        public void Print() {
            //System.out.println("name1 = " + name1);//name1不是静态成员,会报错
            System.out.println("name2 = " + name2);
        }
    }
}

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

package com.hspedu.innerclass;
​
public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
​
​
    }
}
​
class Outer10 {
    private int n1 = 10;
    public String name1 = "Mike";
    private static String name2 = "Jack";
​
    //1、可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员。
    public static class Inner10 {//2、可以添加啊任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
​
        public void Print() {
            //System.out.println("name1 = " + name1);//name1不是静态成员,会报错
            System.out.println("name2 = " + name2);
        }
    }
}

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

package com.hspedu.innerclass;
​
public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.Call();
    }
}
​
class Outer10 {
    private int n1 = 10;
    public String name1 = "Mike";
    private static String name2 = "Jack";
​
    //1、可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员。
    public static class Inner10 {//2、可以添加啊任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
​
        public void Print() {
            //System.out.println("name1 = " + name1);//name1不是静态成员,会报错
            System.out.println("name2 = " + name2);
        }
    }
​
    //3、作用域:同其他的成员,为整个类体。
    public void Call() {
        Inner10 inner10 = new Inner10();
        inner10.Print();
    }
}

输出结果为:

name2 = Jack
​

9.8.11、静态内部类_2(P423)

4、静态内部类---访问-----》外部类(比如:静态属性)[访问方式:直接访问所有静态成员]

5、外部类---访问-----》静态内部类[访问方式:创建对象,再访问]

6、外部其他类---访问---静态内部类

package com.hspedu.innerclass;
​
public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.Call();
​
        //6、外部其他类---访问---静态内部类
        //方式一
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        //由于是静态内部类,可以通过类名直接访问(前提是满足访问权限)
        inner10.Print();
​
        //方式二
        //①编写一个方法返回一个内部类实例
        Outer10.Inner10 inner101 = outer10.getInner10();
        inner101.Print();
        //②
        Outer10.Inner10 inner10_ = outer10.getInner10_();
        inner10_.Print();
    }
}
​
class Outer10 {
    private int n1 = 10;
    public String name1 = "Mike";
    private static String name2 = "Jack";
​
    //1、可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员。
    public static class Inner10 {//2、可以添加啊任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
​
        public void Print() {
            //System.out.println("name1 = " + name1);//name1不是静态成员,会报错
            System.out.println("name2 = " + name2);
        }
    }
​
    //3、作用域:同其他的成员,为整个类体。
    public void Call() {
        Inner10 inner10 = new Inner10();
        inner10.Print();
    }
​
    //6、外部其他类---访问---静态内部类
    //方式二
    //编写一个方法返回一个内部类实例
    public Inner10 getInner10() {
        return new Inner10();
    }
    public static Inner10 getInner10_() {
        return new Inner10();
    }
}

输出结果为:

name2 = Jack
name2 = Jack
name2 = Jack
name2 = Jack
​

7、如果外部类和内部类的成员重名是,内部类访问的话,默认遵循就近原则。如果想访问外部类的成员,则可以使用 外部类名.成员 去访问

package com.hspedu.innerclass;
​
public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.Call();
​
        //6、外部其他类---访问---静态内部类
        //方式一
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        //由于是静态内部类,可以通过类名直接访问(前提是满足访问权限)
        inner10.Print();
​
        //方式二
        //①编写一个方法返回一个内部类实例
        Outer10.Inner10 inner101 = outer10.getInner10();
        inner101.Print();
        //②
        Outer10.Inner10 inner10_ = outer10.getInner10_();
        inner10_.Print();
​
        //7、如果外部类和内部类的成员重名是,内部类访问的话,默认遵循就近原则。如果想访问外部类的成员,则可以使用 外部类名.成员 去访问
        inner10.Visit();
    }
}
​
class Outer10 {
    private int n1 = 10;
    public String name1 = "Mike";
    private static String name2 = "Jack";
​
    //1、可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员。
    public static class Inner10 {//2、可以添加啊任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
        private static String name2 = "张三";
​
        public void Print() {
            //System.out.println("name1 = " + name1);//name1不是静态成员,会报错
            System.out.println("name2 = " + name2);
        }
​
        //7、如果外部类和内部类的成员重名是,内部类访问的话,默认遵循就近原则。如果想访问外部类的成员,则可以使用 外部类名.成员 去访问
        public void Visit() {
            System.out.println("静态内部类name2 = " + name2 + "\n外部类name2 = " + Outer10.name2);
        }
    }
​
    //3、作用域:同其他的成员,为整个类体。
    public void Call() {
        Inner10 inner10 = new Inner10();
        inner10.Print();
    }
​
    //6、外部其他类---访问---静态内部类
    //方式二
    //编写一个方法返回一个内部类实例
    public Inner10 getInner10() {
        return new Inner10();
    }
​
    public static Inner10 getInner10_() {
        return new Inner10();
    }
}

输出结果为:

name2 = 张三
name2 = 张三
name2 = 张三
name2 = 张三
静态内部类name2 = 张三
外部类name2 = Jack
​

9.8.12、内部类课堂练习(P424)

下面代码输出结果为

package com.hspedu.innerclass;
​
public class InnerClassExercise03 {
    public InnerClassExercise03() {
        Inner_ inner_ = new Inner_();
        inner_.a = 10;
        Inner_ inner_1 = new Inner_();
        System.out.println(inner_1.a);
    }
​
    class Inner_ {
        public int a = 5;
    }
​
    public static void main(String[] args) {
        InnerClassExercise03 innerClassExercise03 = new InnerClassExercise03();
        Inner_ inner_ = innerClassExercise03.new Inner_();
        System.out.println(inner_.a);
    }
}

输出结果为:

5
5
​

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值