Java 面向对象(高级)------类方法类变量(static)、main语法、代码块(面试高频问题)、单例设计模式(饿汉式懒汉式:面试)、final关键字、抽象类(模板设计模式)、接口、内部类


类的五大成员:
1、属性
2、方法
3、代码块
4、构造器
5、内部类

类变量

类变量的定义

类变量也叫静态变量/静态属性,是该类的所有对象共享的变量
static变量在类加载时就生成,所以没有创建对象实例也可以使用
类变量的生命周期随类的申明开始,类的消亡而销毁
任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。

类变量的定义和访问方法

定义:访问修饰符 static 数据类型 变量名
访问方法:类名.类变量名或者对象名.类变量名

在这里插入图片描述

类变量的引出

在这里插入图片描述

代码实现

错误示范
count输出为1

package Java_base_study.homework;
//通过小朋友玩游戏,引出类变量问题
public class Class_var {
    
    
    public static void main(String[] args) {
    
    
        Child 小张 = new Child("小张");
        Child 小李 = new Child("小李");
        小张.join();
        小李.join();
        System.out.println(小张.count);
    }
}
class Child{
    
    
    public String name;
    public int count;
    public Child(String name) {
    
    
        this.name = name;
    }
    public void join(){
    
    
        System.out.println(this.name+"加入了游戏");
        count++;
    }
}

注意使用和不使用类变量的区别

package Java_base_study.homework;
//通过小朋友玩游戏,引出类变量问题
public class Class_var {
    
    
    public static void main(String[] args) {
    
    
        //int count=0;//count独立于对象,访问很麻烦,体现不出面向对象
        Child 小张 = new Child("小张");
        Child 小李 = new Child("小李");
        小张.join();
        //count++;
        小张.count++;
        小李.join();
        //count++;
        小李.count++;
        //类变量的count指向同一空间
        System.out.println(Child.count);//2
        System.out.println(小张.count);//2
        System.out.println(小李.count);//2

    }
}
class Child{
    
    
    public String name;
    public static int count;//类对象
    public Child(String name) {
    
    
        this.name = name;
    }
    public void join(){
    
    
        System.out.println(this.name+"加入了游戏");
    }
}

类变量的内存布局

共识:
static变量是同一个类的对象共享
static变量在类加载时就生成了

在这里插入图片描述

类方法

类方法应用场景

当我们希望不申明对象,就能调用该方法时,将其申明为static是很方便的。例如Math,Arrays这些工具类

类方法使用注意

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

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

  3. 普通成员方法,既可以访问普通变量(方法),也可以访问静态变量(方法)。

类方法的定义和调用

在这里插入图片描述

代码实例

static方法只能访问static变量

package Java_base_study.homework;

public class Class_method {
    
    
    public static void main(String[] args) {
    
    
        Stu zhang = new Stu("zhang");
        Stu li = new Stu("li");
        zhang.payfee(100);
        li.payfee(200);
        Stu.showfee();
    }
}
class Stu{
    
    
    private String name;
    private static int fee;

    public Stu(String name) {
    
    
        this.name = name;
    }
    //static 方法只能访问static 变量
    public static void payfee(int fee){
    
    
        Stu.fee+=fee;//fee类变量通过类名调用
    }
    public static void showfee(){
    
    
        System.out.println(fee);
    }
}

main语法

main方法里可以直接调用静态变量和方法
在这里插入图片描述
在这里插入图片描述

代码块

加载类或者创建对象时隐示调用

基本语法

在这里插入图片描述
在这里插入图片描述

代码块使用注意点(重要)

总结:普通代码块创建对象时被调用,static代码块在类加载时调用,类只加载一次
在这里插入图片描述

static 和非static 代码块的区别

总结:普通代码块创建对象时被调用,static代码块在类加载时调用,类只加载一次
普通代码块可以看成是构造器的一部分
static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。
如果是普通代码块,每创建一个对象,就执行一次。
普通代码块,在创建对象实例时,会被隐式调用,创建一次调用一次,只是使用类的静态成员时,普通代码块不执行!!!
我们可以这样理解,在使用类名访问static 变量时(如 Stu.name),类只是被加载而没有创建对象,所以只执行静态代码块
static代码块只能直接调用static成员,而普通代码块可以调用任意成员

类什么时候会被加载问题(非常重要)

类被加载的情况:
1.创造对象实例时
2.创造子类对象实例时,父类也会被加载,且先加载父类再加载子类
3.在使用类的静态变量或者方法时

代码演示:

package Java_base_study.homework;
public class codeblock {
    
    
    public static void main(String[] args) {
    
    
        //创造子类对象实例,注意顺序
        B b = new B();
        System.out.println(b.count);//注意static代码块只会被调用一次这里由于之前调用过,所以不在调用
        System.out.println(D.count);

    }
}
class A{
    
    
    static {
    
    
        System.out.println("调用了A的代码块");
    }
}
class B extends A{
    
    
    static {
    
    
        System.out.println("调用了B的代码块");
    }
    public static int count=1;
}
class C{
    
    
    static {
    
    
        System.out.println("调用了C的代码块");
    }
}
class D extends C{
    
    
    static {
    
    
        System.out.println("调用了D的代码块");
    }
    public static int count=3;
}

输出结果:
在这里插入图片描述

创建一个对象,代码块在一个类中调用的次序(重要,难)

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

第二步:
调用普通代码块和普通属性的初始化
super优先级高于普通代码块子类构造器中隐含super()方法,先去执行父类的构造器中的普通代码块,这里由于是同一个类先不讨论
(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
第三步:
调用构造器

代码举例

package Java_base_study.homework;

public class Main{
    
    
    public static void main(String []args){
    
    
        A2 a=new A2();
    }
}
class A2{
    
    
    private int n2=getn2();
    //静态变量初始化
    public static int n1=getn1();
    {
    
    
        System.out.println("A2的普通代码块被调用");
    }
    //静态代码块
    static{
    
    
        System.out.println("A2的静态代码块被调用");
    }
    public static int getn1(){
    
    
        System.out.println("getn1被调用");
        return 100;
    }
    public int getn2(){
    
    
        System.out.println("getn2被调用");
        return 200;
    }
}

输出:
在这里插入图片描述

构造器中隐藏super和普通构造器,调用顺序?

==构造器的最前面其实隐含了super()和调用普通代码块,先super后普通代码块
静态代码块,属性初始化,在类加载时,就执行完毕因此是优先于构造器和普通代码块执行的 ==
代码:

package Java_base_study.homework;

public class order {
    
    
    public static void main(String[] args) {
    
    
        BBB bbb = new BBB();
    }
}
class AAA{
    
    
    {
    
    
        System.out.println("AAA的普通代码块被调用");
    }
    public AAA(){
    
    
        //super()省略
        System.out.println("调用了AAA的构造器");
    }
}
class BBB extends AAA{
    
    
    {
    
    
        System.out.println("BBB的普通代码块被调用");
    }
    public BBB(){
    
    
        //super()省略
        System.out.println("调用了BBB的构造器");
    }
}

输出:
在这里插入图片描述

综合题:创建一个子类对象时调用顺序(面试)

①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
②子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
③因为super优先级高于普通代码块,所以先回到子类构造器中的super()方法(很有可能隐藏起来了)调用父类构造器先执行父类的(super方法,这里我们认为父类没有爷类了所以可以不执行super)调用普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
④执行完父类的普通代码块和初始化回到父类的构造方法
⑤父类构造方法执行玩,代表子类构造器中super()完成,接着去执行子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
⑥子类的构造方法

例子:

为什么这里的sam!=null?
因为sam它new了一个对象,指向了空间,只能说它的内容为空!!!

在这里插入图片描述

单例设计模式

在这里插入图片描述

饿汉式:

饿汉式单例设计模式(一个类只允许有一个实例),可能没有使用到这个对象,但基于机制已经帮你把对象创建好了
步骤:
将构造器私有化----->防止直接new
在类内部new唯一的对象(private)
定义一个公共的 static方法返回这个对象

代码:

package Java_base_study.homework.SingleExample;
public class SingleExample {
    
    
    public static void main(String[] args) {
    
    
        Girlfriend gf2 = Girlfriend.gether();
        System.out.println(gf1);
        Girlfriend gf2 = Girlfriend.gether();
        System.out.println(gf1==gf2);//true//gether()方法返回的是Girlfriend 类中唯一的私有对象
        System.out.println(Girlfriend.age);//可能没有使用到这个对象,但基于机制已经帮你把对象创建好了
    }
}
class Girlfriend{
    
    
    private String name;
    public static  int age=18;
    private Girlfriend(String name){
    
    
        this.name=name;
    }
    //static 方法只能访问static变量,所以需要申明为static
    //static 变量只在类加载时创建一次不会创建两次,体现唯一性
    private static Girlfriend jisoo=new Girlfriend("jisoo");
    //不用static ,就必须在main中申明对象才能访问gether()方法
    public static Girlfriend gether(){
    
    
        return jisoo;
    }

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

懒汉式

与饿汉式的区别,只有当用户调用gether()方法时才生成一个对象,后面再次使用该方法,返回的任然是同一对象

package Java_base_study.homework.SingleExample;
public class SingleExample {
    
    
    public static void main(String[] args) {
    
    
        Girlfriend gf1 = Girlfriend.gether();
        System.out.println(gf1);
        Girlfriend gf2 = Girlfriend.gether();
        System.out.println(gf2);//只调用一次构造器
        System.out.println(gf1==gf2);//true//gether()方法返回的是Girlfriend 类中唯一的私有对象
    }
}
class Girlfriend{
    
    
    private String name;
    private Girlfriend(String name){
    
    
        System.out.println("调用了构造器");
        this.name=name;
    }
    //static 方法只能访问static变量,所以需要申明为static
    //static 变量只在类加载时创建一次不会创建两次,体现唯一性
    private static Girlfriend jisoo;
    //不用static ,就必须在main中申明对象才能访问gether()方法
    public static Girlfriend gether(){
    
    
        if(jisoo==null){
    
    
            jisoo=new Girlfriend("jisoo");
        }
    return jisoo;
    }

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

输出:
在这里插入图片描述

final关键字

什么时候会用到

在某些情况下,程序员可能有以下需求,就会使用到final:
1)当不希望类被继承时,可以用final修饰.
2)当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。
3)当不希望类的的某个属性的值被修改,可以用final修饰.
4)当不希望某个局部变量被修改,可以使用final修饰

final属性赋值的位置

final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一
定义时:如public final double TAX_RATE=0.08;
在构造器中
在代码块中。

特别注意:
如果final修饰的属性是静态的,则初始化的位置只能是
1、定义时
2、在静态代码块中,不能在构造器中赋值。(因为构造器和对象有关,而静态的属性和对象无关)

注意点

  1. final类不能被继承但可以实例化对象
  2. 如果类不是final类,但含有final方法,则该方法虽无法重写,但可以被继承。
    在这里插入图片描述
  3. 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。//因为本类已经无法继承,再申明为final方法画蛇添足
  4. final不能修饰构造方法(即构造器)
    5. final和static往往搭配使用,效率更高,底层编译器做了优化处理。
  5. 包装类(Integer,Double,Float,Boolean等都是final),String也是final类。
    在这里插入图片描述

抽象类

比如说有小动物类,但是其中的eat()方法,不知道具体动物喜欢吃什么就可以申明为抽象方法
在这里插入图片描述

抽象方法没有方法体,且一个类中还有抽象方法就需要将其申明为抽象类
一般抽象类会被继承,抽象方法会由子类实现,子类申明时不要申明为abstract了

在这里插入图片描述

使用细节:

1)抽象类不能被实例化
2)抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
3)一旦类包含了abstract方法,则这个类必须声明为abstract
4) abstract只能修饰类和方法,不能修饰属性和其它的。
5)如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。
6)抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的。

模板设计模式

在这里插入图片描述
代码:

package Java_base_study.homework.templatedesign;

public abstract class Template {
    
    
    //具体job工作还不知道所有申明为abstract
    public abstract void job();
    public void calculateTime() {
    
    //调用抽象方法,在计算时间方法中有三句话固定
        long start = System.currentTimeMillis();//固定的
        job();//随对象变化而变化
        long end = System.currentTimeMillis();//固定的
        System.out.println("job()方法实现时间" + (end - start));//固定的
    }

}

class manager extends Template {
    
    
    //继承抽象类一定要实现抽象方法,或者把自己申明为抽象类
    public void job() {
    
    
        int temp = 0;
        for (int i = 1; i <= 1000000; i++) {
    
    
            temp += i;
        }
    }
}
class worker extends Template{
    
    
    public void job(){
    
    
        int temp=0;
        for(int i=1;i<=7000000;i++){
    
    
            temp*=i;
        }
    }
}

接口

类实现接口中的抽象方法快捷键:alt+enter
在这里插入图片描述

代码模拟

package Java_base_study.homework.UsbInterface;

interface UsbInterface {
    
    
    public void start();

    public void stop();
}

class computer {
    
    
    public void work(UsbInterface usbInterface) {
    
    
        //通过接口调用方法
        usbInterface.start();
        usbInterface.stop();
    }
}

class phone implements UsbInterface {
    
    
    @Override
    public void start() {
    
    
        System.out.println("手机开始工作");
    }

    @Override
    public void stop() {
    
    
        System.out.println("手机停止工作");
    }
}

class camera implements UsbInterface {
    
    
    @Override
    public void start() {
    
    
        System.out.println("相机开始工作");
    }

    @Override
    public void stop() {
    
    
        System.out.println("相机停止工作");
    }
}

基本介绍

注意点:
1、接口中给出的方法都默认是public、static方法所以不用画蛇添足加public和static
2、接口中也可以写属性(只能是final的,且由public final static 修饰还必须得初始化)
3、类在实现接口时要把接口的抽象方法都实现!!!
4、实现(使用)类中可以有自己的属性和方法
5、Jdk8以前接口中都是抽象方法,8以后可以有默认方法,但是要写static 和default
6、接口不可以实例化

在这里插入图片描述

注意点

1、一个类同时可以实现多个接口 class camera implements A,B{}
2、接口中的属性,只能是final的,而且是 public static final修饰符。比如:
int a=1;实际上是 public static final int a=1;(必须初始化)

3、接口中属性的访问形式:接口名.属性名
4、接口不能继承其它的类,但是可以继承多个别的接口
interface A extends B,C{}
5、接口的修饰符只能是public 和默认,这点和类的修饰符是一样的。
在这里插入图片描述

课堂练习

变量a的属性是public static final 的,虽然没法被继承但是是public的,所以可以访问
在这里插入图片描述

接口和继承类比较

接口是对java单继承机制的补充-------->继承只能让小猴子像老猴子一样,而接口可以让小猴子像小鸟一样飞
当子类继承了父类,就自动的拥有父类的功能
如果子类需要扩展功能,可以通过实现接口的方式扩展

在这里插入图片描述

接口的多态特性

在这里插入图片描述

多态参数

在这里插入图片描述

多态数组

在这里插入图片描述
这里使用了向下转型,来调用子类特有的方法call()

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

        //多态数组 -> 接口类型数组
        Usb[] usbs = new Usb[2];
        usbs[0] = new Phone_();
        usbs[1] = new Camera_();
        /*
        给Usb数组中,存放 Phone  和  相机对象,Phone类还有一个特有的方法call(),
        请遍历Usb数组,如果是Phone对象,除了调用Usb 接口定义的方法外,
        还需要调用Phone 特有方法 call
         */
        for(int i = 0; i < usbs.length; i++) {
    
    
            usbs[i].work();//动态绑定..
            //和前面一样,我们仍然需要进行类型的向下转型
            if(usbs[i] instanceof Phone_) {
    
    //判断他的运行类型是 Phone_
                ((Phone_) usbs[i]).call();
            }
        }

    }
}

interface Usb{
    
    
    void work();
}
class Phone_ implements Usb {
    
    
    public void call() {
    
    
        System.out.println("手机可以打电话...");
    }

    @Override
    public void work() {
    
    
        System.out.println("手机工作中...");
    }
}
class Camera_ implements Usb {
    
    

    @Override
    public void work() {
    
    
        System.out.println("相机工作中...");
    }
}

多态传递现象

在这里插入图片描述
在这里插入图片描述
和继承父类很类似
在这里插入图片描述

课堂练习

在这里插入图片描述

interface A {
    
    
    int x = 0;
}//想到等价public static final int x = 0;

class B {
    
    
    int x = 1;
    static int x1=2;
}//普通属性

class C extends B implements A {
    
    
    public void px()

    {
    
    
        //System.out.println(x);//错误此处的x模糊不知道调用A中的x还是类B中的
        //解决方法
        System.out.println(A.x);
        //类B中的属性是protected,访问需要创建对象或者super方法
        System.out.println(new B().x);
        System.out.println(super.x);
        //若变量属性是static则可以使用类名直接调用
        System.out.println(B.x1);
    }

    public static void main(String[]args) {
    
    
        new C().px();
    }
}

接口小结

在这里插入图片描述

内部类

基本介绍

在这里插入图片描述

分类(4种)

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

1)局部内部类(有类名)
2)匿名内部类(没有类名,重点!!!)

局部内部类(相当于局部变量)

1、虽然是两个不同的类,但可以直接访问外部类的所有成员,包含私有的
2、不能添加访问修饰符,因为它的地位就是一个局部变量(类方法中定义的变量,作用域局限于这个方法)。
局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
3、作用域只在定义它的方法或者代码块中
4、内部类可以直接访问外部类成员,但是外部类怎么调用内部类的方法
5、外部类无法直接访问局部内部类(就像他无法直接访问局部变量一样)
6、如果外部类和局部内部类的成员重名时,默认遵循就近原则,
如果想访问外部类的成员,则可以使用(外部类名.this.成员------->本质就是外部类的一个对象)去访问

class Outer02 {
    
    //外部类
    private int n1 = 100;
    public void m1() {
    
    //方法
//局部内部类是定义在外部类的局部位置,通常在方法
        class Inner02 {
    
    //局部内部类(本质仍然是一个类)
        //1、可以直接访问外部类的所有成员,包含私有的public void f1() 
        //2、class Inner02不能添加访问修饰符,但可以用final修饰
        //3、作用域只在定义它的方法或者代码块中
        private int n1=800;//与外部类重名(故意)
            {
    
    //如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
                System.out.println("n1=" + n1+"外部类的n1="+Outer02.this.n1);//采用就近原则输出800、100
            }
        }
    }
    //4.外部类在方法中,可以创建Inner02对象,然后调用方法即可
    Inner02 inner02 = new Inner02();
    inner02.f1();
}

匿名内部类(重要!!!!)

记住四大要点:(1)本质是类(2)内部类(3)该类没有名字(4)同时还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
匿名内部类可以直接访问外部类成员

匿名内部类的基本语法
new类或接口(参数列表){
类体
};
总结:创建类和实例化对象同时完成,且在这之后销毁这个类仅保留对象
匿名内部类用来实现接口中的方法,重写类中的方法

基于接口的匿名内部类

想使用IA接口,并创建对象怎么实现?
传统方式,是写一个类,实现该接口,并创建对象
现在要求是 Tiger/Dog 类只是使用一次,后面再不使用这样我们创建一个类并重写方法,成本太高了
可以使用匿名内部类来简化开发,匿名类只能使用一次
代码必须看有知识点

public class InnerClass {
    
    
    public static void main(String[] args) {
    
    
        Outer04 outer04 = new Outer04();
        outer04.method();
    }
}
class Outer04 {
    
     //外部类
    private int n1 = 10;//属性
    public void method() {
    
    //方法
        //基于接口的匿名内部类
        //1.需求: 想使用IA接口,并创建对象
        //2.传统方式,是写一个类,实现该接口,并创建对象
        IA tiger1 =new tiger();
        tiger1.cry();
        //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();
        tiger.cry();//匿名类只能使用一次,但是生成的对象可以反复调用

    }
}
interface  IA{
    
     void cry();}
class tiger implements IA{
    
    
    @Override
    public void cry() {
    
    
        System.out.println("老虎叫唤。。。");
    }
}
基于类、抽象类的匿名内部类

Father father=new Father(“Jack”);//创建对象
Father father=new Father(“Jack”){
};//构造匿名内部类

public class InnerClass {
    
    
    public static void main(String[] args) {
    
    
        Outer04 outer04 = new Outer04();
        outer04.method();
    }
}

class Outer04 {
    
     //外部类
    private int n1 = 10;//属性
    public void method() {
    
    //方法
        //演示基于类的匿名内部类
        //分析
        //1. father编译类型 Father
        //2. father运行类型 Outer04$2
        //3. 底层会创建匿名内部类
        /*
            class Outer04$2 extends Father{
                @Override
                public void test() {
                    System.out.println("匿名内部类重写了test方法");
                }
            }
         */
        //4. 同时也直接返回了 匿名内部类 Outer04$2的对象
        //5. 注意("jack") 参数列表会传递给 构造器
        Father father = new Father("jack") {
    
    
            @Override
            public void test() {
    
    
                System.out.println("匿名内部类重写了test方法");
            }
        };
        System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2
        father.test();

        //基于抽象类的匿名内部类
        Animal animal = new Animal() {
    
    
            @Override
            void eat() {
    
    
                System.out.println("小狗吃骨头...");
            }
        };
        animal.eat();
    }
}
class Father {
    
    //类
    public Father(String name) {
    
    //构造器
        System.out.println("接收到name=" + name);
    }
    public void test() {
    
    //方法
    }
}

abstract class Animal {
    
     //抽象类
    abstract void eat();
}
调用eat()的两种方法

法一:

Animal animal = new Animal() {
    
    
            @Override
            void eat() {
    
    
                System.out.println("小狗吃骨头...");
            }
        };
        animal.eat();//使用动态绑定创建对象
        

法二:直接调用

        //调用eat()的第二种方法直接调用
        new Animal(){
    
    
            @Override
            void eat() {
    
    
                System.out.println("小狗吃骨头...");
            }
        }.eat();
匿名内部类的使用场景

1、当作实参传递,简洁高效
使用传统方法需要定义实现接口的类,一般多次使用才需要这么做
有了匿名类我们直接利用匿名类生成一个对象传递给方法当作实参,方便

public class Test {
    
    
    public static void main(String[] args) {
    
    
        outclass outclass = new outclass();
        outclass.f1(new camera());//定义实现类,传统方法
        outclass.f1(new Usb() {
    
    
            @Override
            public void use() {
    
    
                System.out.println("使用匿名类实现方法重载");
            }
        });

    }
}
class outclass {
    
    
    public static void f1(Usb usb){
    
    
        usb.use();
    }
}
interface Usb{
    
    
    void use();
}
class camera implements Usb{
    
    
    @Override
    public void use() {
    
    
        System.out.println("使用传统方法实现方法重载");
    }
}

练习:

//测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
//再传入另一个匿名内部类(对象),打印:小伙伴上课了
public class InnerClassExercise02 {
    
    
    public static void main(String[] args) {
    
    
        Cellphone cellphone = new Cellphone();
        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.ring();
    }
}

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

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

与局部内部类不同,成员内部类可以添加访问修饰符,他就像类的成员一样
可以直接访问外部类的所有成员(包括私有)
作用域:整个类体
在这里插入图片描述
外部类访问成员内部类,只要创建对象就能访问
外部其他类访问成员内部类的方法:
法一:Out out =new Out();
Innter innter =out.new Innter();
法二:在外部类中定义一个获取成员内部类对象的方法
public Innter getInnter(){
return new Innter();
}
Innter innter=new Out.getInnter();
在这里插入图片描述
外部类和内部类成员重名
在这里插入图片描述

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

可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
可以添加任意访问修饰符(public.protected、默认、private),因为它的地位就是一个成员。
作用域同其他的成员,为整个类体
在这里插入图片描述
外部其他类访问静态内部类的方法与上面类似(不再重复)
如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则
如果想访问外部类的成员,则可以使用(外部类名.成员)(注意这里特殊在于不用外部类名.this.成员了,因为它是静态的)去访问

练习:
请添加图片描述

猜你喜欢

转载自blog.csdn.net/qq_52934831/article/details/120464427