(韩顺平)10 面向对象编程(高级部分)(自用)

1 0 面向对象编程(高级部分)

10.1 类变量和类方法

10.1.1 类变量-提出问题

提出问题的主要目的就是让大家思考解决之道,从而引出我要讲的知识点. 说:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?,编写程序解决。

10.1.2 传统的方法来解决

10.1.3 类变量快速入门

思考: 如果,设计一个 int count 表示总人数,我们在创建一个小孩时,就把 count 加 1,并且 count 是所有对象共享的 就 ok 了!,我们使用类变量来解决 ChildGame.java 改进

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 + " 加入了游戏..");
    }
}

10.1.4 类变量内存布局

下面的示意图帮助理解,静态变量放在哪?

有些书说在方法区...,jdk 版本有关系,记住一点:static变量是对象共享

不管static变量在哪里,共识

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

(2)static类变量,在类加载的时候就生成了

10.1.5 什么是类变量

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

10.1.6如何定义类变量

定义语法

访问修饰符 static 数据类型 变量名;【推荐】

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

10.1.7 如何访问类变量 VisitSt

package com.hspedu.static_;
​
public class VisitStatic {
    public static void main(String[] args) {
        //类名.类变量名
        //说明:类变量是随着类的加载而创建,所以即使没有创建对象示例也可以访问
        System.out.println(A.name);
        A a = new A();
        System.out.println("a.name=" + a.name);
    }
}
​
class A{
    //类变量
    //类变量的访问。必须遵守相关的访问权限
    public static String name = "Jim";
}

10.1.8 类变量使用注意事项和细节讨论 StaticDetail.java

  1. 什么时候需要类变量

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

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

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

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

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

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

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

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

package com.hspedu.static_;
​
public class StaticDetail {
    public static void main(String[] args) {
        //System.out.println(B.n1);
        System.out.println(B.n2);
​
        //静态变量是类加载的时候就创建了,所以我们没有创建对象实例
        //也可以通过类名.类变量名来访问
        System.out.println(C.address);//北京
    }
}
​
class B{
    public  int n1 = 100;
    public static int n2 = 200;
}
class C{
    public static String address = "北京";
}

10.1.9 类方法基本介绍

10.1.10 类方法的调用

package com.hspedu.static_;
​
public class StaticMethod {
    public static void main(String[] args) {
        //创建2个学生对象,交学费
        Stu tom = new Stu("tom");
        tom.payFee(100);
​
        Stu marry = new Stu("marry");
        marry.payFee(200);
​
        //输出当前收到的总学费
        Stu.showFee();
    }
}
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);
    }
}

10.1.12 类方法经典的使用场景

10.1.13 类方法使用注意事项和细节讨论 StaticMethodDetail.java

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

    类方法中无this的参数

    普通方法中隐含this的参数

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

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

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

  5. 类方法(静态方法)中只能访问 静态变量 或静态方法。

  6. 普通成员方法,及可以访问 非静态成员,也可以访问静态成员。

小结:静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员。(必须访问访问权限)

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

10.1.14 课堂练习

10.1.15 题 2(评讲),看看下面代码有没有错误,如果有错误,就修改,看看输出什么?

10.1.16 题 3(评讲),看看下面代码有没有错误,如果有错误,就修改,看看 total 等于多少 4?

10.2 理解 main 方法语

10.2.1 深入理解 main 方法

【举例说明】:

10.2.2 特别提示:

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

  2. 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静 态成员,[举例说明] Main01.java

package com.hspedu.main_;
​
public class Main01 {
    //静态的变量/属性
    private static String name = "Jim";
    //非静态属性
    private int n1 = 10000;
​
    //静态方法
    public static void hi(){
        System.out.println("Main01 的hi方法");
    }
​
    public void cry(){
        System.out.println("Main01 的cry方法");
    }
    public static void main(String[] args) {
        //可以直接使用 name
        //1.静态方法main 可以访问本类的静态成员
        System.out.println("name=" + name);
        hi();
        //2.静态方法main 不可以访问本类的非静态成员
        //System.out.println("n1=" + n1);错误
        //3.静态方法main 要访问本类的非静态成员,需要先创建对象,在调用即可
        Main01 main01 = new Main01();
        System.out.println(main01.n1);
        main01.cry();
    }
​
}
​

10.2.3 案例演示

10.3 代码块

10.3.1 基本介绍

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

但和方法不同,没有方法名,没有返回,没有参数,只有方法体,额日期额不用通过对象火雷显式调用,而是加载类时,或创建对象是隐性调用

10.3.2 基本语法

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

package com.hspedu.codeblock_;
​
public class CodeBlock01 {
    public static void main(String[] args) {
        Movie movie = new Movie("你好,李焕英");
        Movie movie1 = new Movie("唐人街探案", 30, "陈思诚");
    }
}
​
class Movie {
    private String name;
    private double price;
    private String director;
​
    //3个构造器-》重载
    //解读:
    //(1)下面的三个构造器都有相同的语句
    //(2)这样代码看起来比较冗余
    //(3)这时我们可以把相同的语句,放在一个代码块中,即可
    //(4)这样当我们不管调用那个构造器创建对象,都回先调用代码块的内容
    //(5)代码块调用的顺序优先于构造器
​
​
    {
        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;
    }
}

代码运行结果:

由代码运行结果可得出:

代码块的调用顺序是优于构造器的

10.3.4 代码块使用注意事项和细节讨论 CodeBlockDetail01.java

package com.hspedu.codeblock_;
​
public class CodeBlockDetail01 {
    public static void main(String[] args) {
​
        //类被加载的情况举例
        //1. 创建对象实例(new)
        //AA aa1 = new AA();
        //2.创建了子类对象示例,父类也会被加载,而且,父类先被加载,子类后被加载
        //AA aa2 = new AA();
        //3.使用类的静态成员时(静态属性,静态方法)
        //System.out.println(Cat.n1);
​
        //static代码块,是在类加载时,执行的,而且指挥执行一次
//        DD dd = new DD();
//        DD dd1 = new DD();
​
        //普通的代码块,在创建对象实例时,会被隐式地调用
        //被创建一次,就会调用一次,
        //如果知识使用类的静态成员,普通代码块并不会被执行
​
        System.out.println(DD.n1);//8888,静态代码块一定会执行,
​
    }
}
​
class DD{
    public static int n1 = 8888;//静态属性
    //静态代码块
    static {
        System.out.println("DD 的静态代码1被执行...");
    }
    //普通代码块,new 对象时,被调用,而且是没创建一次,就调用一次
    //可以简单的,理解 普通代码块是对构造器的补充
    {
        System.out.println("DD 的普通代码块..");
    }
}
class Animals{
    //静态代码块
    static {
        System.out.println("Animals 的静态代码1被执行...");
    }
}
​
class Cat extends Animals{
​
    public static int n1 = 999;//静态属性
​
    //静态代码块
    static {
        System.out.println("Cat 的静态代码1被执行...");
    }
}
class BB {
    //静态代码块
    static {
        System.out.println("BB 的代码块1被调用...");
    }
}
​
class AA extends BB{
​
    static {
        System.out.println("AA 的静态代码块1被执行");
    }
}

package com.hspedu.codeblock_;

public class CodeBlockDetail02 {
    public static void main(String[] args) {
        A a = new A();
    }
}

class A {
    private int n2 = getN2();//普通属性初始化

    {//普通代码块
        System.out.println("A 普通代码块01");
    }
    //静态属性初始化
    private static int n1 = getN1();

    static {//静态代码块
        System.out.println("A 静态代码01");
    }

    public static int getN1(){
        System.out.println("getN1()调用..");
        return 100;
    }

    public int getN2(){//普通方法/非静态
        System.out.println("getN2被调用...");
        return 200;
    }
    //无参构造器
    public A(){
        System.out.println("A() 构造器被调用");
    }
}

代码执行结果

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

代码执行结果

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

10.3.5 课堂练习题 CodeBlockExercise01.java

//题 1:下面的代码输出什么?1min
package com.hspedu.codeblock_;

public class CodeBlockExercise01 {
    public static void main(String[] args) {
        System.out.println("total = "+ Person.total); //100
        System.out.println("total = "+ Person.total); //100
    }
}
class Person {
    public static int total;//静态变量
    static {//静态代码块
        total = 100;
        System.out.println("in static block!");//(1)
    }
}

代码执行结果

10.4 单例设计模式

10.4.1 设计模式是什么

10.4.2 单例模式简介

10.4.3 应用实例

10.4.4 饿汉模式

package single_;
​
/**
 * 演示懒汉式的单例模式
 * */
public class SingleTon02 {
    public static void main(String[] args) {
//        System.out.println(Cat.n1);
        Cat instance = Cat.getInstance();
        System.out.println(instance);
    }
}
class Cat{
    private String name;
    public static int n1 = 999;
    public static Cat cat;
​
    //步骤:
    //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 = new Cat("大黄");
        }
        return cat;
    }
​
    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

10.4.5 懒汉模式

package single_;

/**
 * 演示懒汉式的单例模式
 * */
public class SingleTon02 {
    public static void main(String[] args) {
//        System.out.println(Cat.n1);
        Cat instance = Cat.getInstance();
        System.out.println(instance);
    }
}
class Cat{
    private String name;
    public static int n1 = 999;
    public static Cat cat;

    //步骤:
    //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 = new Cat("大黄");
        }
        return cat;
    }

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

10.4.6 饿汉式 VS 懒汉式

10.5 final 关键字

10.5.1 基本介绍

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

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

10.6 抽象类

10.6.1 先看一个问题

10.6.2 解决之道-抽象类快速入门

代码:

package absttact_;
​
public class Abstract01 {
    public static void main(String[] args) {
​
    }
}
​
abstract class Animals{
    private String name;
    private int age;
​
    //思考:这里eat 这里你实现了,其实没有什么意义
    //即: 父类方法不确定性的问题
    //===> 考虑将该方法设计为抽象(abstract)方法
    //===> 所谓抽象方法就是没有实现的方法
    //===> 所谓没有实现就是指,没有方法体
    //===> 当一个类中存在抽象方法时,需要将该类声明为abstract类
    //===> 一般来说,抽象类会被继承,有其子类来实现抽象方法.
    public Animals(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    public abstract void cry();
}

10.6.3 抽象类的介绍

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

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

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

10.7.1 基本介绍

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

10.7.3 最佳实践

package absttact_;

public abstract class Template {
    public abstract void job();//抽象方法

    public void calculateTime(){
        //得到开始的时间
        long start = System.currentTimeMillis();
        job(); //动态绑定机制
        //得的结束的时间
        long end = System.currentTimeMillis();
        System.out.println("任务执行时间 " + (end - start));
    }
}

package absttact_;

public class AA extends Template{
    @Override
    public void job() {
        long num = 0;
        for (long i = 1; i <= 8000000; i++) {
            num += i;
        }
    }
}

package absttact_;

public class BB extends Template{
    @Override
    public void job() {
        long num = 0;
        for (long i = 1; i <= 800000; i++) {
            num *= i;
        }
    }
}

package absttact_;

public class TestTemplate {
    public static void main(String[] args) {
        AA aa = new AA();
        aa.calculateTime();

        BB bb = new BB();
        bb.calculateTime();
    }
}

10.8 接口

10.8.1 基本介绍

注:在jdk8后,可以有默认实现方法,需要使用default关键字修饰

10.8.2 深入讨论

代码:

package interface_;
​
public interface DbInterface {
    public void connect();//连接方法
    public void close();//关闭方法
}
​
package interface_;
​
public class MysqlDb implements DbInterface{
    @Override
    public void connect() {
        System.out.println("连接Mysql数据库");
    }
​
    @Override
    public void close() {
        System.out.println("关闭Mysql数据库");
    }
}
​
package interface_;
​
public class OracleDb implements DbInterface{
    @Override
    public void connect() {
        System.out.println("连接Oracle数据库");
    }
​
    @Override
    public void close() {
        System.out.println("关闭Oracle数据库");
    }
}
​
package interface_;
​
public class Interface01 {
    public static void main(String[] args) {
        MysqlDb mysqlDb = new MysqlDb();
        t(mysqlDb);
        OracleDb oracleDb = new OracleDb();
        t(oracleDb);
    }
​
    public static void t(DbInterface dbInterface){
        dbInterface.connect();
        dbInterface.close();
    }
}

10.8.3 注意事项和细节

接口中的属性,只能是final的,而且是 public static final修饰符

10.8.4课堂练习

10.8.5 实现接口 vs 继承类

小结:当子类继承了父类,就自动的拥有父类的功能

如果子类需要扩展功能,可以通过实现接口的方式扩展,可以理解 实现接口 是 对 java 单继承机制的一种补充.

10.8.6 接口的多态特性

类似于向上转型

package interface_;

public class InterfacePolyParameter {
    public static void main(String[] args) {
        //接口类型的多态体现
        //接口类型的变量if01 可以指向 实现了IF接口类的对象实例
        IF if01 = new Boat();
        if01 = new Car();
    }
}

interface IF{}
class Boat implements IF{}
class Car implements IF{}

该处体现类似向下转型

package interface_;
​
/*
给 Usb 数组中,存放 Phone 和 相机对象,Phone 类还有一个特有的方法 call(),
请遍历 Usb 数组,如果是 Phone 对象,除了调用 Usb 接口定义的方法外,
还需要调用 Phone 特有方法 call
*/
public class InterfacePolyArr {
    public static void main(String[] args) {
        Usb usb[] = new Usb[2];
        usb[0] = new Camera_();
        usb[1] = new Phone_();
        for (int i = 0; i < usb.length; i++) {
            usb[i].work();
            if(usb[i] instanceof Phone_){
                ((Phone_) usb[i]).call();
            }
        }
​
    }
}
​
interface Usb{
    public void work();
}
​
class Phone_ implements Usb{
    @Override
    public void work() {
        System.out.println("手机工作中...");
    }
    public void call(){
        System.out.println("手机通话中...");
    }
}
​
class Camera_ implements Usb{
    @Override
    public void work() {
        System.out.println("相机工作中...");
    }
}

package interface_;

public class nterfacePolyPass {
    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() {
    }
}

10.8.9 课堂练习

public class InterfaceExercise02 {
    public static void main(String[] args) {
​
    }
}
​
interface A {  // 1min 看看
    int x = 0;
}  //想到 等价 public static final int x = 0;
​
class B {
    int x = 1;
} //普通属性
​
class C extends B implements A {
    public void pX() {
        //System.out.println(x); //错误,原因不明确x
        //可以明确的指定x
        //访问接口的 x 就使用 A.x
        //访问父类的 x 就使用 super.x
        System.out.println(A.x + " " + super.x);
    }
​
    public static void main(String[] args) {
        new C().pX();
    }
}
​
​

10.9 内部类

如果定义类在局部位置(方法中/代码块) :(1) 局部内部类 (2)匿名内部类

定义在成员位置 (1) 成员内部类 (2)

10.9.1 基本介绍

10.9.2 基本语法

10.9.3 内部类的分类

10.9.4 局部内部类的使用 LocalInnerClass.java

package interclass;
​
/*
* a演示局部内部类
* */
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("Outer 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();
    }
}

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

package interclass;
​
public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer01 outer = new Outer01();
        outer.method();
    }
}
​
class Outer01 {
    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. 匿名内部类使用一次,就不能再使用
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎哭哭...");
            }
        };
        System.out.println("tiger的运行类型" + tiger.getClass());
        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 = new Father("Jack"){
            @Override
            public void test() {
                System.out.println("匿名内部类重写了test方法");
            }
        };
        System.out.println("father对象的运行类型=" + father.getClass());
        father.test();
​
        //基于抽象类的内部类
        Animals animals = new Animals(){
            @Override
            void eat() {
                System.out.println("小狗吃骨头");
            }
        };
    }
}
​
interface IA{//接口
    public void cry();
}
//class Tiger 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 Animals {//抽象类
    abstract void eat();
}

package interclass;
​
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() {
                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
//        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);
    }
}

10.9.6 匿名内部类的最佳实践

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

package interclass;
​
public class InnerClassExercise01 {
    public static void main(String[] args) {
        f1(new IL() {
            @Override
            public void show() {
                System.out.println("这是一幅名画...");
            }
        });
       
    }
​
    public static void f1 (IL il){
        il.show();
    }
}
​
interface IL{
    void show();
}
public class Interface01 {
    public static void main(String[] args) {
        Picture p = new Picture();
        f1(p);
    }
     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...");
    }
}

10.9.8 匿名内部类课堂练习

package interclass;
​
public class InnerClassExercise02 {
    public static void main(String[] args) {
        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类型
        bell.ring();
    }
}
​

10.9.9 成员内部类的使用

package interclass;
/*
* 测试成员内部类
* */
public class MenberInnerClass01 {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();
        //外部其他类,使用成员内部类的三种方式
        //老韩解读
        // 第一种方式
        // outer08.new Inner08();相当于吧 new Inner08()当作是outer08的成员
        // 这就是一个语法,不要特别的纠结.
        Outer08.Inner08 inner01 = outer08.new Inner08();
        //第二种方式,编写一个方法,可以返回 Inner08对象
        Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
        inner08Instance.say();
    }
}

class Outer08{
    private int n1 = 100;
    public String name = "张三";
    class Inner08{
        public void say(){
            System.out.println("我是你爹 " + "n1 =" + n1);
        }
    }
    public void t1(){
        Inner08 inner08 = new Inner08();
        inner08.say();
    }
    //方法,返回Inner08实例
    public Inner08 getInner08Instance(){
        return new Inner08();
    }
}

10.9.9 静态内部类的使用 StaticInnerClass01.java

package interclass;

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

        //外部其他类 使用静态内部类
        //方式一
        //因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();

        //方式二
        //编写一个方法,可以返回静态内部类的对象实例
        Outer10.Inner10 inner101 = outer10.getInner10();
        System.out.println("=============");
        inner101.say();

        Outer10.Inner10 inner102 = Outer10.getInner10_();//不用再实例化对象
        System.out.println("*********");
        inner102.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 = "小燕";
        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();
    }

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值