Day07_多态、final关键字、权限修饰符、内部类、

1.多态

在这里插入图片描述

1.在多态的代码当中,成员变量的访问规则是:

多态 =上转型  //多态和上转型是等价的

成员变量:编译看左边,运行还看左边。(左边和右边指的是等号的左边和等号的右边)
成员方法:编译看左边,运行看右边。(左边和右边指的是等号的左边和等号的右边)

成员变量:编译看左边,运行还看左边。
Fu obj = new Zi()为例:编译的时候看等号左边“Fu obj”所以obj只能调用父类有的变量,obj.num是父类有的变量,但是obj.age就不行因为父类没有这个变量
运行的时候obj.num结果是多少呢?你要看的还是等号左边父类的值
public class Fu  {
    
    

    int num = 10;

    public void showNum() {
    
    
        System.out.println(num);
    }

}

public class Zi extends Fu {
    
    

    int num = 20;

    int age = 16;
    
    @Override
    public void showNum() {
    
    
        System.out.println(num);
    }
}



public class Demo01MultiField {
    
    

    public static void main(String[] args) {
    
    
        
        Fu obj = new Zi();
        System.out.println(obj.num); //10
        System.out.println(obj.age); //错误!
        //编译的时候看左“Fu obj”所以obj只能调用父类有的变量,obj.num是父类有的变量,但是obj.age就不行因为父类没有这个变量。运行的时候obj.num结果是多少呢?你要看的还是左边父类的值
        
        
        obj.showNum();//20
		//涉及到成员方法的访问规则,见下面。
    }

}

2.在多态的代码当中,成员方法的访问规则是:

成员变量:编译看左边,运行还看左边。(左边和右边指的是等号的左边和等号的右边)
成员方法:编译看左边,运行看右边。(左边和右边指的是等号的左边和等号的右边)

成员方法:编译看左边,运行看右边。
Fu obj = new Zi()为例:编译的时候看左“Fu obj”所以obj只能调用父类有的方法和变量,obj.method()和obj.methodFu()都是父类有的方法,但是obj.methodZi()就不行因为父类没有这个方法
运行的时候obj.method()和obj.methodFu()结果是多少呢?你要看右边也就是子类
public class Fu {
    
    

    int num = 10;

    public void showNum() {
    
    
        System.out.println(num);
    }

    public void method() {
    
    
        System.out.println("父类方法");
    }

    public void methodFu() {
    
    
        System.out.println("父类特有方法");
    }

}
public class Zi extends Fu {
    
    

    int num = 20;

    int age = 16;

    @Override
    public void showNum() {
    
    
        System.out.println(num);
    }

    @Override
    public void method() {
    
    
        System.out.println("子类方法");
    }

    public void methodZi() {
    
    
        System.out.println("子类特有方法");
    }
}
public class Demo02MultiMethod {
    
    

    public static void main(String[] args) {
    
    
        Fu obj = new Zi(); // 多态

        obj.method(); 
        obj.methodFu();
        obj.methodZi(); // 错误写法!
        //Fu obj = new Zi()为例:编译的时候看左“Fu obj”所以obj只能调用父类有的方法和变量,obj.method()和obj.methodFu()都是父类有的方法,但是obj.methodZi()就不行因为父类没有这个方法
        //运行的时候obj.method()和obj.methodFu()结果是多少呢?你要看右边也就是子类
    }

}

3.多态和继承

在这里插入图片描述

4.上转型和下转型

在这里插入图片描述

Animal animal = new Cat(); // 上转型,new Cat()就是cat的一个对象,现在把cat的一个对象new Cat()当作了Animal
Cat cat = (Cat) animal;//下转型,(Cat) animal就是把本来的猫的animal还原为Cat
public abstract class Animal {
    
    

    public abstract void eat();

}

public class Cat extends Animal {
    
    
    @Override
    public void eat() {
    
    
        System.out.println("猫吃鱼");
    }

    // 子类特有方法
    public void catchMouse() {
    
    
        System.out.println("猫抓老鼠");
    }
}

public class Dog extends Animal {
    
    
    @Override
    public void eat() {
    
    
        System.out.println("狗吃SHIT");
    }

    public void watchHouse() {
    
    
        System.out.println("狗看家");
    }
}

public class Demo01Main {
    
    

    public static void main(String[] args) {
    
    
。
        Animal animal = new Cat(); // 上转型
        animal.eat(); // 猫吃鱼
        animal.catchMouse(); // 错误写法!
        //对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。编译看左运行看右嘛,左侧animal没有catchMouse()方法

        // 向下转型,进行“还原”动作,本来就是猫才能还原为猫,现在不把猫当作animal看待了
        Cat cat = (Cat) animal;
        cat.catchMouse(); // 猫抓老鼠

        Dog dog = (Dog) animal; 错误的向下转型,下转型不是随便转的,它必须是一个“还原”的动作
        // animal=new Cat()   本来new的时候这个animal是一只猫,现在非要当做狗
    }

}

5.instanceof的使用

如何知道这个animal是狗转过来的呢还是猫转过来的呢?用instanceof

对象 instanceof 类名称
这将会得到一个boolean值结果,也就是判断前面的对象能不能当做后面类型的实例。
public abstract class Animal {
    
    

    public abstract void eat();

}

public class Cat extends Animal {
    
    
    @Override
    public void eat() {
    
    
        System.out.println("猫吃鱼");
    }

    // 子类特有方法
    public void catchMouse() {
    
    
        System.out.println("猫抓老鼠");
    }
}

public class Dog extends Animal {
    
    
    @Override
    public void eat() {
    
    
        System.out.println("狗吃SHIT");
    }

    public void watchHouse() {
    
    
        System.out.println("狗看家");
    }
}

public class Demo02Instanceof {
    
    

    public static void main(String[] args) {
    
    
        Animal animal = new Dog(); // 本来是一只狗
        animal.eat(); // 狗吃SHIT

        // 如果希望掉用子类特有方法,需要向下转型
        // 判断一下父类引用animal本来是不是Dog
        if (animal instanceof Dog) {
    
    
            Dog dog = (Dog) animal;
            dog.watchHouse();
        }
        // 判断一下animal本来是不是Cat
        if (animal instanceof Cat) {
    
    
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }

        giveMeAPet(new Dog());
    }

    public static void giveMeAPet(Animal animal) {
    
    
        if (animal instanceof Dog) {
    
    
            Dog dog = (Dog) animal;
            dog.watchHouse();
        }
        if (animal instanceof Cat) {
    
    
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
    }

}

6.笔记本案例

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

public interface USB {
    
    

    public abstract void open(); // 打开设备

    public abstract void close(); // 关闭设备

}
// 鼠标就是一个USB设备
public class Mouse implements USB {
    
    
    @Override
    public void open() {
    
    
        System.out.println("打开鼠标");
    }

    @Override
    public void close() {
    
    
        System.out.println("关闭鼠标");
    }

    public void click() {
    
    
        System.out.println("鼠标点击");
    }
}
// 键盘就是一个USB设备
public class Keyboard implements USB {
    
    
    @Override
    public void open() {
    
    
        System.out.println("打开键盘");
    }

    @Override
    public void close() {
    
    
        System.out.println("关闭键盘");
    }

    public void type() {
    
    
        System.out.println("键盘输入");
    }
}

public class Computer {
    
    

    public void powerOn() {
    
    
        System.out.println("笔记本电脑开机");
    }

    public void powerOff() {
    
    
        System.out.println("笔记本电脑关机");
    }

    // 使用USB设备的方法,使用接口作为方法的参数
    public void useDevice(USB usb) {
    
    
        usb.open(); // 打开设备
        if (usb instanceof Mouse) {
    
     // 一定要先判断
            Mouse mouse = (Mouse) usb; // 向下转型
            mouse.click();
        } else if (usb instanceof Keyboard) {
    
     // 先判断
            Keyboard keyboard = (Keyboard) usb; // 向下转型
            keyboard.type();
        }
        usb.close(); // 关闭设备
    }

}
public class DemoMain {
    
    

    public static void main(String[] args) {
    
    
        // 首先创建一个笔记本电脑
        Computer computer = new Computer();
        computer.powerOn();

        // 首先进行向上转型
        USB usbMouse = new Mouse(); // 多态写法
        // 参数是USB类型,我正好传递进去的就是USB鼠标
        computer.useDevice(usbMouse);

        // 创建一个USB键盘
        Keyboard keyboard = new Keyboard(); // 没有使用多态写法
        // 方法参数是USB类型,传递进去的是实现类对象
        computer.useDevice(keyboard); // 正确写法!也发生了向上转型
        // 使用子类对象,匿名对象,也可以
//        computer.useDevice(new Keyboard()); // 也是正确写法

        computer.powerOff();

    }

}

我要说的一点事:

public class DemoMain {
    
    

    public static void main(String[] args) {
    
    
    
        method(10.0); // 正确写法,double --> double
        method(20); // 正确写法,int --> double

    }

    public static void method(double num) {
    
    
        System.out.println(num);
    }

}
/*
method(方法要求传参的是一个double但你传进来了int型,不报错,因为int自动转为double是安全的)

同样的,在上面的USB实例中,useDevice(USB usb)要求传参的是一个USB类型的,而你computer.useDevice(new Keyboard())却传参的是keyboard的对象,
这也没关系,因为此时keyboard自动转为USB属于上传型也是安全的
*/

2.final关键字

final关键字代表最终、不可改变的,而且没有子孙、不能被继承。

常见四种用法:

  1. 可以用来修饰一个类
  2. 可以用来修饰一个方法
  3. 还可以用来修饰一个局部变量
  4. 还可以用来修饰一个成员变量

1.final用来修饰一个类

final关键字用来修饰一个类的时候,格式:
public final class 类名称 {
    
    
    // ...
}

含义:当前这个类不能有任何的子类。(太监类)
注意:一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写(因为没儿子。)
public final class MyClass /*extends Object*/{
    
    
	//我们说过任何类都是Object的子类,当让包括final类
    public void method() {
    
    
        System.out.println("方法执行!");
    }
}

// 不能使用一个final类来作为父类
public class MySubClass /*extends MyClass*/ {
    
    
}

2.final用来修饰一个方法

当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆盖重写。
格式:

修饰符 final 返回值类型 方法名称(参数列表) {
    
    
    // 方法体
}
public abstract class Fu {
    
    

    public final void method() {
    
    
        System.out.println("父类方法执行!");
    }

    public abstract /*final*/ void methodAbs() ;
    //对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾。
}

public class Zi extends Fu {
    
    
    @Override
    public void methodAbs() {
    
    

    }

    // 错误写法!不能覆盖重写父类当中final的方法
//    @Override
//    public void method() {
    
    
//        System.out.println("子类覆盖重写父类的方法!");
//    }
}

3.final用来修饰一个局部变量

        // 一旦使用final用来修饰局部变量,那么这个变量就不能进行更改。
        // “一次赋值,终生不变”
        final int num2 = 200;

        num2 = 250; // 错误写法!不能改变!
// 对于基本类型来说,不可变说的是变量当中的数据不可改变
// 对于引用类型来说,不可变说的是变量当中的地址值不可改变
public class Student {
    
    

    private String name;

    public Student() {
    
    
    }

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

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }
}


public class Demo01Final {
    
    

    public static void main(String[] args) {
    
    
        final Student stu2 = new Student("高圆圆");
        stu2 = new Student("赵又廷");// 错误写法!final的引用类型变量,其中的地址不可改变
        
        stu2.setName("高圆圆圆圆圆圆");  //这是可以的,并没有改变地址值

    }
}

4.final用来修饰一个成员变量

对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变。

  1. 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。
  2. 对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。

①直接赋值

public class Person {
    
    

    private final String name = "鹿晗";
    //你一旦直接赋值后下面试图改变name的setName(String name)\Person(String name)都会报错

    public Person() {
    
    
        
    }

    public Person(String name) {
    
    
        this.name = name; //报错,因为你在修改name值
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;//报错,因为你在修改name值
    }
}

②构造方法赋值
使用构造方法赋值时必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。

//下面有两个构造方法,那么这两个构造方法必须都有对final name赋值的语句,这样才会不论你调用哪个构造方法final name都有初值
public class Person {
    
    

    private final String name;

    public Person() {
    
    
        name = "关晓彤";
    }

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

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;//报错,因为你在修改name值
    }
}

3.四种权限修饰符

Java中有四种权限修饰符:
                    public  >   protected   >   (default)   >   private
同一个类(我自己)        YES         YES             YES             YES
同一个包(我邻居)        YES         YES             YES             NO
同一包子类(亲儿子)        YES         YES             YES             NO
不同包子类(私生子)       YES         YES             NO              NO
不同包非子类(陌生人)      YES         NO              NO              NO

注意事项:(default)并不是关键字“default”,而是根本不写,你写上default反而会报错。

在同一个包里,不能访问private
在这里插入图片描述
什么叫做不同包子类?
在这里插入图片描述

在这里插入图片描述
注意事项:

public > protected > (default) > private
定义一个类的时候,权限修饰符规则:
1. 外部类:public / (default)
2. 成员内部类:public / protected / (default) / private
3. 局部内部类:什么都不能写

class Outer {
    
     //外部类要么public要么(default)

    public void methodOuter() {
    
    
        class Inner {
    
     // 局部内部类,什么都不写

        }
    }
    
    public class Inner2 {
    
     // 成员内部类public / protected / (default) / private
		System.out.println("成员内部类");
    }

}

4.内部类

内部类好比:身体和心脏的关系。汽车和发动机的关系。

分类:

  1. 成员内部类
  2. 局部内部类(包含匿名内部类)

1.成员内部类

修饰符 class 外部类名称 {
    
    
    修饰符 class 内部类名称 {
    
    
        // ...
    }
    // ...
}

注意:内用外,随意访问;外用内,需要内部类对象。
//也就是说内部随意访问外面的东西,但外面不能随意访问内部的

==========================
如何使用成员内部类?有两种方式:
1. 间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。
2. 直接方式,公式:
类名称 对象名 = new 类名称();
【外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
public class Body {
    
     // 外部类

    public class Heart {
    
     // 成员内部类
        // 内部类的方法
        public void beat() {
    
    
            System.out.println("心脏跳动:蹦蹦蹦!");
            System.out.println("我叫:" + name); // 正确写法!
        }
    }


    // 外部类的成员变量
    private String name;//尽管是private但可以被内部类访问

    // 外部类的方法
    public void methodBody() {
    
    
        System.out.println("外部类的方法");
        new Heart().beat(); //外用内,需要创建内部类对象。
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }
}

public class Demo01InnerClass {
    
    

    public static void main(String[] args) {
    
    
        Body body = new Body(); // 外部类的对象
        
        // 通过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart
        body.methodBody();

        // 【外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();】
        Body.Heart heart = new Body().new Heart();
        heart.beat();
    }
}

2.成员内部类重名问题

// 如果出现了重名现象,那么格式是:外部类名称.this.外部类成员变量名
public class Outer {
    
    

    int num = 10; // 外部类的成员变量

    public class Inner /*extends Object*/ {
    
    

        int num = 20; // 内部类的成员变量

        public void methodInner() {
    
    
            int num = 30; 
            System.out.println(num); // 30
            System.out.println(this.num); // 20
            System.out.println(super.num);//错误,内部类和外部类可不是继承关系
            System.out.println(Outer.this.num); // 【外部类名称.this.外部类成员变量名】10
        }

    }
}

public class Demo02InnerClass {
    
    

    public static void main(String[] args) {
    
    
        // 外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
        Outer.Inner obj = new Outer().new Inner();
        obj.methodInner();
    }

}

3.局部内部类。

如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。

定义格式:
修饰符 class 外部类名称 {
    
    
    修饰符 返回值类型 外部类方法名称(参数列表) {
    
    
        class 局部内部类名称 {
    
    
            // ...
        }
    }
}
“局部”性:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。

class Outer {
    
    

    public void methodOuter() {
    
    
        class Inner {
    
     // 局部内部类
            int num = 10;
            public void methodInner() {
    
    
                System.out.println(num); // 10
            }
        }

        Inner inner = new Inner();
        inner.methodInner();
    }

}

public class DemoMain {
    
    

    public static void main(String[] args) {
    
    
        Outer obj = new Outer();
        obj.methodOuter();
    }

}

4.局部内部类的注意事项。

/*
局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【final类型的】,final可以省略。
 */
public class MyOuter {
    
    

    public void methodOuter() {
    
    
        int num = 10; // 因为局部内部类访问了它,它事实上是final类型,不能再次赋值的。

        class MyInner {
    
    
            public void methodInner() {
    
    
                System.out.println(num);
            }
        }
    }

}

5.匿名内部类(本节内容最最重要的知识点!!)

如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,
那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。

匿名内部类的定义格式:
接口名称 对象名 = new 接口名称() {
    
    
    // 覆盖重写所有抽象方法
};

另外还要注意几点问题:

  1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
    如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。
  2. 匿名对象,在【调用方法】的时候,只能调用唯一一次。
    如果希望同一个对象,调用多次方法,那么必须给对象起个名字。
  3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
    强调:匿名内部类和匿名对象不是一回事!!!
public interface MyInterface {
    
    

    void method1(); // 抽象方法

    void method2();

}
public class MyInterfaceImpl implements MyInterface {
    
    
    @Override
    public void method1() {
    
    
        System.out.println("实现类覆盖重写了方法!111");
    }

    @Override
    public void method2() {
    
    
        System.out.println("实现类覆盖重写了方法!222");
    }
}

public class DemoMain {
    
    

    public static void main(String[] args) {
    
    

        //方法一,传统的上转型方法,适合多次调用的,匿名只适合使用一次的
        MyInterface obj = new MyInterfaceImpl();
        obj.method1();

        
        //这是错误写法
        MyInterface obj1 = new MyInterface(); // 错误写法!接口可不能直接new对象


        // 使用匿名内部类,但不是匿名对象,对象名称就叫objA
        MyInterface objA = new MyInterface() {
    
    
            @Override
            public void method1() {
    
    
                System.out.println("匿名内部类实现了方法!111-A");
            }

            @Override
            public void method2() {
    
    
                System.out.println("匿名内部类实现了方法!222-A");
            }
        };
        objA.method1();
        objA.method2();
        System.out.println("=================");

        
        // 使用了匿名内部类,而且省略了对象名称,也是匿名对象
        new MyInterface() {
    
    
            @Override
            public void method1() {
    
    
                System.out.println("匿名内部类实现了方法!111-B");
            }

            @Override
            public void method2() {
    
    
                System.out.println("匿名内部类实现了方法!222-B");
            }
        }.method1();

        
        // 因为匿名对象无法调用第二次方法,所以要使用method2()需要再创建一个匿名内部类的匿名对象
        new MyInterface() {
    
    
            @Override
            public void method1() {
    
    
                System.out.println("匿名内部类实现了方法!111-B");
            }

            @Override
            public void method2() {
    
    
                System.out.println("匿名内部类实现了方法!222-B");
            }
        }.method2();
    }

}

6.实例

public interface Skill {
    
    

    void use(); // 释放技能的抽象方法

}
public class SkillImpl implements Skill {
    
    
    @Override
    public void use() {
    
    
        System.out.println("Biu~biu~biu~");
    }
}
public class Hero {
    
    

    private String name; // 英雄的名称
    private Skill skill; // 英雄的技能

    public Hero() {
    
    
    }

    public Hero(String name, Skill skill) {
    
    
        this.name = name;
        this.skill = skill;
    }

    public void attack() {
    
    
        System.out.println("我叫" + name + ",开始施放技能:");
        skill.use(); // 调用接口中的抽象方法
        System.out.println("施放技能完成。");
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public Skill getSkill() {
    
    
        return skill;
    }

    public void setSkill(Skill skill) {
    
    
        this.skill = skill;
    }
}
public class DemoGame {
    
    

    public static void main(String[] args) {
    
    
        Hero hero = new Hero();
        hero.setName("艾希"); // 设置英雄的名称

        // 方法一:设置英雄技能
        hero.setSkill(new SkillImpl()); // 使用单独定义的实现类

        //方法二:假装SkillImpl不存在的情况下,还可以改成使用匿名内部类
        Skill skill = new Skill() {
    
    
            @Override
            public void use() {
    
    
                System.out.println("Pia~pia~pia~");
            }
        };
        hero.setSkill(skill);

        // 方法三:进一步简化,同时使用匿名内部类和匿名对象
        hero.setSkill(new Skill() {
    
    
            @Override
            public void use() {
    
    
                System.out.println("Biu~Pia~Biu~Pia~");
            }
        });

        hero.attack();
    }

}

5.发红包案例

p212—p214(day11_18-----day11_20)

猜你喜欢

转载自blog.csdn.net/weixin_45014721/article/details/114946695