【多态、内部类、权限修饰符、代码块】

day02【static、接口、多态、内部类】

今日内容

  • static—>必须掌握
    • 修饰成员变量
    • 修饰成员方法
    • 修饰代码块
  • 接口—>必须掌握
    • 定义接口
    • 实现接口
    • 接口中成员访问特点
  • 多态—>必须掌握
    • 实现多态
    • 多态时成员访问特点
    • 多态的好处和弊端
    • 引用类型转换
    • 多态的应用场景
  • 内部类
    • 匿名内部类的使用—>必须掌握
  • 引用类型使用小结

第一章 static关键字

1.static关键字

1.1 概述

  • 概念: static是一个关键字,表示静态的意思,可以用来修饰成员变量,成员方法,代码块

1.2 修饰成员变量

格式:
  • 格式: 修饰符 static 数据类型 变量名;
特点:
  • 被static修饰的成员变量叫做静态成员变量\类变量
  • 被static修饰的成员变量会被该类的所有对象共享
  • 如果该类的某个对象修改了静态成员变量的值,那么该类的所有对象使用的都是修改后的值
访问方式:
  • 对象名.静态成员变量名
  • 类名.静态成员变量名 ------->推荐\掌握
案例:
public class Chinese {
    // 非静态成员变量
    String name;

    // 静态成员变量
    static String country;

    public Chinese(String name,String country) {
        this.name = name;
        this.country = country;
    }

    public Chinese() {
    }
}


public class Test {
    public static void main(String[] args) {
        /*
            - 格式: 修饰符 static 数据类型 变量名;
            特点:
                - 被static修饰的成员变量叫做静态成员变量\类变量
                - 被static修饰的成员变量会被该类的所有对象共享
                - 如果该类的某个对象修改了静态成员变量的值,那么该类的所有对象使用的都是修改后的值
            访问方式:
            - 对象名.静态成员变量名
            - 类名.静态成员变量名   ------->推荐\掌握
         */
        // 创建Chinese对象
        Chinese c1 = new Chinese("张三","中华人民共和国");
        // 姓名:张三,国籍:中华人民共和国
        System.out.println("姓名:"+c1.name+",国籍:"+c1.country);

        System.out.println("=======");

        // 创建Chinese对象
        Chinese c2 = new Chinese();
        // 姓名:null,国籍:中华人民共和国
        System.out.println("姓名:"+c2.name+",国籍:"+c2.country);

        System.out.println("=======");
        // 如果该类的某个对象修改了静态成员变量的值,那么该类的所有对象使用的都是修改后的值
        c2.country = "中国";
        // 姓名:张三,国籍:中国
        System.out.println("姓名:"+c1.name+",国籍:"+c1.country);
        // 姓名:null,国籍:中国
        System.out.println("姓名:"+c2.name+",国籍:"+c2.country);

        System.out.println("=======");
        System.out.println(Chinese.country);// 中国

    }
}

1.3 修饰成员方法

格式:
修饰符 static 返回值类型 方法名(形参列名){
    方法体
}
特点:
  • 被static修饰的成员方法叫做静态方法
访问方式:
  • 对象名.方法名(实参);
  • 类名.方法名(实参);------->推荐\掌握
案例:
public class Chinese {

    public void show1(){
        System.out.println("非静态方法 show1...");
    }

    public static void show2(){
        System.out.println("静态方法 show2...");
    }
}

public class Test {
    public static void main(String[] args) {
        /*
            修饰成员方法
         */
        Chinese.show2();// 推荐

        System.out.println("======");

        Chinese c = new Chinese();
        c.show1();
        c.show2();
    }
}

1.4 静态方法调用的注意事项

  • 静态方法中不能出现this关键字

  • 静态方法中只能直接访问静态成员变量和静态成员方法

  • 静态方法中不能直接访问非静态成员变量和非静态成员方法

  • 非静态方法中可以直接访问一切成员变量和成员方法

    public class Chinese {
        // 非静态成员变量
        String name;
        // 静态成员变量
        static String country;
    
        // 静态成员方法
        public static void method1(){
            // 静态方法中不能出现this关键字
            // System.out.println(this.name);
    
            // 静态方法中不能直接访问非静态成员变量和非静态成员方法
            // System.out.println(name);
            // show1();
    
            // 静态方法中只能直接访问静态成员变量和静态成员方法
            System.out.println(country);
            method2();
        }
    
        public static void method2(){
            System.out.println("静态成员方法method2...");
        }
    
        // 非静态方法
        public void show1(){
            System.out.println("非静态成员方法show1...");
        }
    
        public void show2(){
            // 访问非静态成员
            System.out.println(name);
            show1();
    
            // 访问静态成员
            System.out.println(country);
            method2();
        }
    }
    
    
    public class Test {
        public static void main(String[] args) {
            /*
                静态方法中不能出现this关键字
                静态方法中只能直接访问静态成员变量和静态成员方法
                静态方法中不能直接访问非静态成员变量和非静态成员方法
                非静态方法中可以直接访问一切成员变量和成员方法
    
             */
            Chinese.method1();
    
            System.out.println("=====");
            // Chinese c = new Chinese();
            // c.show2();
    
            new Chinese().show2();
        }
    }
    
    

1.5 修饰代码块

格式:
static {
    
}
// 静态代码块
位置:
  • 类中方法外(类的成员位置)
执行:
  • 随着类的加载而执行,并且只执行一次
  • 类的加载: 第一次使用该类的时候,就会加载,并且只加载一次
使用场景:
  • 如果程序中某段代码只需要执行一次,就可以把该段代码放入静态代码块中
  • eg: 加载驱动,读取配置文件中的数据…
案例
public class Chinese {

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

    public Chinese(){
        System.out.println("Chinese 空参构造...");
    }

}


 */
public class Test {
    public static void main(String[] args) {
        System.out.println("开始");
        Chinese c1 = new Chinese();
        Chinese c2 = new Chinese();
        System.out.println("结束");
    }
}

1.6 以后开发中static的应用

概述

以后的项目中,通常会需要一些“全局变量”或者“全局的工具方法”,这些全局变量和方法,可以单独定义在一个类中,并声明为static(静态)的,可以很方便的通过类名访问

例如
public class MathUtils {
    // ctrl+shift+u
    public static final double PI = 3.14;
    public static final int WIDTH = 600;
    public static final int HEIGHT = 800;

    public static int getMax(int[] arr){
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max){
                max = arr[i];
            }
        }
        return max;
    }

}

public class Test {
    public static void main(String[] args) {
        // 需求:在一个工具类中,定义一个π变量和获取int数组最大值方法
        System.out.println(MathUtils.PI);// 3.14

        int[] arr = {1,23,45,23,434,54,32,65,76};
        System.out.println(MathUtils.getMax(arr));// 434
    }
}

第二章 接口

3.1 概述

  • 概述: 接口是Java语言中的一种引用类型,主要是用来存储方法的
  • 接口中的成员:
    • 常量(jdk7及其以前)
    • 抽象方法(jdk7及其以前)
    • 默认方法和静态方法(JDK 8)
    • 私有方法(jdk9)
  • 定义接口需要使用interface关键字—>编译后也会产生class文件
  • 接口没有构造方法,也不能创建对象,所以一般都是供类实现的(implements),类似继承
  • 实现接口的类叫做实现类,实现类如果是普通类必须重写接口中所有的抽象方法,否则实现类是一个抽象类

3.2 定义格式

格式
public interface 接口名称 {
    // 常量
    // 抽象方法
    // 默认方法
    // 静态方法
    // 私有方法
}
案例
public interface IA {

    // 常量:使用public static final修饰,而这三个修饰符可以省略不写
    public static final int NUM = 10;

    // 抽象方法:使用public abstract修饰,而这两个修饰符可以省略不写
    public abstract void method1();

    // 默认方法: 使用public default修饰,public可以省略不写,default不可以省略
    public default void method2(){
        System.out.println("IA 默认方法method2");
    }

    // 静态方法:使用public static修饰,public可以省略不写,static不可以省略
    public static void method3(){
        System.out.println("IA 静态方法method3");
    }

    // 私有方法: 使用private修饰,private不可以省略
    // private void method4(){}
    // private static void method5(){}

}

3.3 实现接口

实现概述
  • 概述:类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。
实现格式:
  • 单实现

    public class 实现类名 implements 接口名{}
    
  • 多实现

    public class 实现类名 implements 接口名1,接口名2,接口名3,...{}
    
  • 先继承后实现

    public class 实现类名 extends 父类名 implements 接口名1,接口名2,接口名3,...{}
    
案例演示:
interface IA{}
interface IB{}
interface IC{}
class Fu{}

// 单实现
class Imp1 implements IA{}
// 多实现
class Imp2 implements IA,IB,IC{}
// 先继承后实现
class Zi extends Fu implements IA,IB,IC{}

3.4 接口中成员的访问特点

  • 接口中成员的访问特点:
        接口中常量: 主要供接口名直接访问,也可以被实现类继承
        接口中抽象方法:就是供实现类重写的
        接口中默认方法:可以供实现类对象直接调用,或者在实现类中重写
        接口中静态方法:只供接口名直接访问,不可以被实现类继承
        接口中私有方法:只能在接口内部方法中调用,不可以被实现类继承
    
  • 案例:

    public interface IA {
        // 常量: 使用public static final修饰
        public static final int NUM = 10;
    
        // 抽象方法: 使用public abstract修饰
        public abstract void method1();
    
        // 默认方法: 使用public default修饰
        public default void method2(){
            System.out.println("IA接口的默认方法method2");
            // method4();
        }
    
        // 静态方法: 使用public static修饰
        public static void method3(){
            System.out.println("IA接口的静态方法method3");
        }
    
        // 私有方法: 使用private修饰
         //private void method4(){}
         //private static void method5(){}
    }
    
    
    
    public class Imp implements IA {
        @Override
        public void method1() {
            System.out.println("实现类重写接口中的抽象方法method1");
        }
    
        @Override
        public void method2() {
            System.out.println("实现类重写接口中的默认方法method2");
        }
    }
    
    
    
    public class Test {
        public static void main(String[] args) {
            /*
                接口中成员的访问特点:
                    接口中常量: 主要供接口名直接访问,也可以被实现类继承
                    接口中抽象方法:就是供实现类重写的
                    接口中默认方法:可以供实现类对象直接调用,或者在实现类中重写
                    接口中静态方法:只供接口名直接访问,不可以被实现类继承
                    接口中私有方法:只能在接口内部方法中调用,不可以被实现类继承
    
             */
            // 访问接口中的常量
            System.out.println(IA.NUM);// 10
            System.out.println(Imp.NUM);// 10
    
            // 访问接口中的抽象方法
            Imp imp = new Imp();
            imp.method1();
    
            // 访问接口中的默认方法
            imp.method2();
    
            // 访问接口中的静态方法
            IA.method3();
        }
    }
    
    

3.5 多实现时的几种冲突情况

公有静态常量的冲突
interface IA{
    public static final int A = 10;
    public static final int B = 10;
}
interface IB{
    public static final int A = 20;
}

class Imp implements IA,IB{

}

public class Test {
    public static void main(String[] args) {
        // 多实现常量冲突: 多个接口中同名的常量不能被实现类继承
        // System.out.println(Imp.A);// 编译报错
        System.out.println(Imp.B);// 10
    }
}

公有抽象方法的冲突
interface IA{
   public abstract void show();
}
interface IB{
    public abstract void show();
}

class Imp implements IA,IB{

    @Override
    public void show() {
        System.out.println("重写一次show方法...");
    }
}

public class Test {
    public static void main(String[] args) {
        // 抽象方法冲突: 必须重写一次抽象方法
    }
}

公有默认方法的冲突
interface IA{
    public default void show(){
        System.out.println("IA show...");
    }
}
interface IB{
    public default void show(){
        System.out.println("IB show...");
    }
}

class Imp implements IA,IB{
    @Override
    public void show() {
        System.out.println("重写一次show方法");
    }
}
public class Test {
    public static void main(String[] args) {
        // 默认方法的冲突: 必须重写一次默认方法
        Imp imp = new Imp();
        imp.show();
    }
}

公有静态方法的冲突
interface IA{
    public static void show(){
        System.out.println("IA show...");
    }
}
interface IB{
    public static void show(){
        System.out.println("IB show...");
    }
}

class Imp implements IA,IB{
    
}
public class Test {
    public static void main(String[] args) {
        // 静态方法的冲突: 不存在冲突,因为静态方法不能被实现类继承
    }
}

私有方法的冲突
  • 不存在冲突,因为私有方法不能被实现类继承

3.6 接口和接口的关系

接口与接口之间的关系
  • 接口和接口之间是继承关系

  • 单继承

    public interface A{}
    public interface B extends A{}
    
  • 多继承

    public interface A{}
    public interface B{}
    public interface C extends A,B{}
    
  • 多层继承

    public interface A{}
    public interface B extends A{}
    public interface C extends B{}
    
接口继承接口的冲突情况
公有静态常量的冲突 `
interface IA{
    public static final int B = 10;
    public static final int C = 10;
}
interface IB{
    public static final int B = 20;
}

interface IC extends IA,IB{
    
}

public class Test {
    public static void main(String[] args) {
        // 常量冲突: 多个接口中同名的常量不会被子接口继承,不同名的会被继承
        // System.out.println(IC.B);// 编译报错
        System.out.println(IC.C);// 10
    }
}

公有抽象方法冲突
interface IA{
    public abstract void show();
}
interface IB{
    public abstract void show();
}

interface IC extends IA,IB{

}

class Imp implements IC{

    @Override
    public void show() {
        System.out.println("重写一次即可");
    }
}

public class Test {
    // 抽象方法冲突: 多个父接口中有相同的抽象方法,子接口只会继承一个抽象方法
}

公有默认方法的冲突
interface IA{
    public default void show(){
        System.out.println("IA show...");
    }
}
interface IB{
    public default void show(){
        System.out.println("IB show...");
    }
}

interface IC extends IA,IB{
    @Override
    default void show() {
        System.out.println("必须重写一次同名的默认方法");
    }
}

public class Test {
    // 默认方法冲突: 多个父接口中有相同的默认方法,子接口必须重写默认方法(default)
    public static void main(String[] args) {
        
    }
}

公有静态方法和私有方法
  • 不存在冲突,因为静态方法和私有方法不会被子接口继承

    interface IA{
        public static void show(){
            System.out.println("IA show...");
        }
    }
    interface IB{
        public static void show(){
            System.out.println("IB show...");
        }
    }
    
    interface IC extends IA,IB{
      
    }
    public class Test {
        public static void main(String[] args) {
            // 静态方法冲突: 子接口无法继承父接口的静态方法,所以不存在冲突
        }
    }
    

3.7 继承的同时又实现存在的问题

父类和接口的公有静态常量的冲突
interface IA{
    public static final int A = 10;
}
class Fu{
    public static final int A = 20;
}

class Imp extends Fu implements IA{
    
}
public class Test {
    public static void main(String[] args) {
        // 常量冲突: 实现类不会继承有冲突的常量
        // System.out.println(Imp.A);// 编译报错
    }
}
父类和接口的抽象方法冲突
interface IA {
    public abstract void show();
}

abstract class Fu {
    public abstract void show();
}

class Imp extends Fu implements IA {

    @Override
    public void show() {
        System.out.println("必须重写一次");
    }
}

public class Test {
    // 抽象方法冲突: 实现类必须重写一次有冲突的抽象方法
}

父类和接口的公有默认方法的冲突
interface IA {
    public default void show(){
        System.out.println("IA show");
    }
}

abstract class Fu {
    public  void show(){
        System.out.println("Fu show");
    }
}

class Imp extends Fu implements IA {

}

public class Test {
    public static void main(String[] args) {
        // 默认方法的冲突: 优先访问父类的
        Imp imp = new Imp();
        imp.show();// Fu show
    }
}

父类和接口的公有静态方法
interface IA {
    public static void show(){
        System.out.println("IA show");
    }
}

abstract class Fu {
    public static void show(){
        System.out.println("Fu show");
    }
}

class Imp extends Fu implements IA {

}

public class Test {
    public static void main(String[] args) {
        // 静态方法的冲突: 不存在冲突,使用的是父类的静态方法
        Imp.show();// Fu show
    }
}

父类和接口的私有方法
  • 不存在冲突

3.8 抽象类和接口的练习

需求:

通过实例进行分析和代码演示抽象类和接口的用法。

1、举例:

​ 犬: 抽象父类

​ 行为:吼叫;吃饭;

​ 缉毒犬:继承犬类,实现缉毒的接口

​ 行为:吼叫;吃饭;缉毒;

​ 缉毒的接口: 缉毒的功能

抽象类: 如果同一群事物共同的功能就定义在抽象类中

接口: 如果不同类型的事物共同拥有的功能就定义在接口中,让这些事物的类实现该接口

实现:
  • Dog

    // 抽象父类
    public abstract class Dog {
        // 吼叫
        public abstract void houJiao();
    
        // 吃饭
        public abstract void eat();
    
    }
    
    
  • JiDu

    public interface JiDu {
        public abstract void jiDu();
    }
    
    
  • JiDuDog

    public class JiDuDog extends Dog implements JiDu{
        @Override
        public void houJiao() {
            System.out.println("汪汪汪...");
        }
    
        @Override
        public void eat() {
            System.out.println("吃狗粮...");
        }
    
    
        @Override
        public void jiDu() {
            System.out.println("使用鼻子闻来缉毒...");
        }
    }
    
  • Test

    public class Test {
        public static void main(String[] args) {
            // 创建缉毒犬对象
            JiDuDog jd = new JiDuDog();
            jd.eat();
            jd.jiDu();
            jd.houJiao();
        }
    }
    
    

第三章 多态

3.1 概述

  • 多态是面向对象三大特征之一。
  • 多态指对于同一行为,对于不同的对象,具有不同的表现形式。在程序中,表示对同一方法,不同的对象有不同实现。
  • 实现多态的前提条件
    • 继承或者实现
    • 父类引用指向子类对象 eg: Fu f = new Zi();
    • 方法的重写(没有重写方法多态是没有意义的)

3.2 实现多态

  • 父类的引用指向子类的对象:

    • 格式: 父类类型 变量名 = new 子类类型(实参);
    • eg: Fu f = new Zi();
  • 接口的引用指向实现类的对象:

    • 格式: 接口名 变量名 = new 实现类名(实参);
    • eg: IA a = new Imp();
  • 继承时的多态:

    abstract class Animal{
        public abstract void eat();
    }
    
    class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨头...");
        }
    }
    
    class Cat extends Animal{
        @Override
        public void eat() {
            System.out.println("猫吃鱼...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            /*
                实现多态的三个条件:
                    1.继承
                    2.父类的引用指向子类的对象
                    3.方法的重写
             */
            // 多态
            Animal anl = new Dog();
            anl.eat();
    
            anl = new Cat();
            anl.eat();
        }
    }
    
    
  • 实现时的多态:

    interface IA{
        void show();
    }
    
    class Imp implements IA{
    
        @Override
        public void show() {
            System.out.println("实现类重写的show...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            /*
                实现多态的三个条件:
                    1.实现
                    2.接口的引用指向实现类的对象
                    3.方法的重写
             */
            // 多态
            IA a = new Imp();
            a.show();
        }
    }
    

3.3 多态时访问成员的特点

  • 1.成员变量: 编译看左边(父类),运行看左边(父类)
  • 2.成员方法:
    • 非静态成员方法:编译看左边(父类),运行看右边(子类)
    • 静态成员方法: 编译看左边(父类),运行看左边(父类)
    • 记忆: 除了非静态成员方法编译看左边,运行看右边,其余都看左边
class Fu{
    int num = 10;

    public void method1(){
        System.out.println("Fu 非静态方法method1...");
    }

    public static void method2(){
        System.out.println("Fu 静态方法method2...");
    }
}

class Zi extends Fu{
    int num = 20;

    public void method1(){
        System.out.println("Zi 非静态方法method1...");
    }

    public static void method2(){
        System.out.println("Zi 静态方法method2...");
    }
}

public class Test {
    public static void main(String[] args) {
        /*
            多态时访问成员的特点:
                1.成员变量: 编译看左边(父类),运行看左边(父类)
                2.成员方法:
                    非静态成员方法:编译看左边(父类),运行看右边(子类)
                    静态成员方法:  编译看左边(父类),运行看左边(父类)
                    
               除了非静态成员方法编译看左边,运行看右边,其余都看左边
         */
        // 父类的引用指向子类的对象
        Fu f = new Zi();
        System.out.println(f.num);// 10

        f.method1();// Zi 非静态方法method1...
        f.method2();// Fu 静态方法method2...

    }
}

3.4 多态的几种表现形式

  • 普通父类多态

    public class Fu{}
    public class Zi extends Fu{}
    public class Test{
        public static void main(String[] args){
            Fu f = new Zi();// 多态
        }
    }
    
  • 抽象父类多态

    public abstract class Fu{}
    public class Zi extends Fu{}
    public class Test{
        public static void main(String[] args){
            Fu f = new Zi();// 多态
        }
    }
    
  • 父接口多态

    public interface IA{}
    public class Imp implements IA{}
    public class Test{
        public static void main(String[] args){
            IA a = new Imp();// 多态
        }
    }
    

3.5 多态的应用场景

  • 变量多态 -----> 意义不大

    class Animal{
        public void eat(){
            System.out.println("吃东西...");
        }
    }
    
    class Dog extends Animal{
        public void eat(){
            System.out.println("狗吃骨头...");
        }
    }
    
    class Cat extends Animal{
    
    }
    
    
    public class Test {
        public static void main(String[] args) {
            // 父类的引用指向子类的对象
            Animal anl1 = new Dog();// 变量多态
            Animal anl2 = new Cat();// 变量多态
    
            // 使用anl1变量一定要根据多态时成员访问特点去使用
            anl1.eat();// 狗吃骨头...
        }
    }
    
  • 形参多态----> 常用

    • 结论: 参数的类型是父类类型,那么就可以接收该父类类型的对象以及该父类类型的所有子类对象

      class Animal{
          public void eat(){
              System.out.println("吃东西...");
          }
      }
      
      class Dog extends Animal{
          public void eat(){
              System.out.println("狗吃骨头...");
          }
      }
      
      class Cat extends Animal{
          public void eat(){
              System.out.println("猫吃鱼...");
          }
      }
      
      public class Test {
          public static void main(String[] args) {
              Dog d = new Dog();
              method(d);// 狗吃骨头...
      
              System.out.println("======");
      
              Cat c = new Cat();
              method(c);// 猫吃鱼...
          }
      
          // 定义一个方法,既可以接收Dog对象也可以接收Cat对象,甚至Animal类对象以及Animal类其他子类对象,并且在方法中调用eat方法
          // 形参多态: 参数的类型是父类类型,那么就可以接收该父类类型的对象以及该父类类型的所有子类对象
          // 实参赋值给形参的时候构成了多态
          public static void method(Animal anl){
              anl.eat();
          }
      
      
          // 需求:定义一个方法,可以接收Dog对象,并且在方法中调用eat方法
          /*public static void method1(Dog dog){
              dog.eat();
          }*/
      
          // 需求:定义一个方法,可以接收Cat对象,并且在方法中调用eat方法
          /*public static void method2(Cat cat){
              cat.eat();
          }*/
      
          // ...
      }
      
      
  • 返回值多态—> 常用

    • 返回值类型为父类类型,那么就可以返回该父类类型的对象,或者该父类类型的子类对象
    class Animal {
        public void eat() {
            System.out.println("吃东西...");
        }
    }
    
    class Dog extends Animal {
        public void eat() {
            System.out.println("狗吃骨头...");
        }
    }
    
    class Cat extends Animal {
        public void eat() {
            System.out.println("猫吃鱼...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 接收返回值的时候其实就是多态: Animal anl1 = new Dog();
            Animal anl1 = method1("Dog");
            anl1.eat();// 狗吃骨头...
    
            System.out.println("=====");
    
            // 接收返回值的时候其实就是多态: Animal anl2 = new Cat();
            Animal anl2 = method1("Cat");
            anl2.eat();// 猫吃鱼...
        }
    
        // 定义一个方法,可以返回Dog对象,也可以返回Cat对象,也可以返回Animal对象,或者Animal类的其他子类对象
        // 返回值多态: 返回值类型为父类类型,那么就可以返回该父类类型的对象,或者该父类类型的子类对象
        public static Animal method1(String classType) {
            //return new Dog();
            //return new Cat();
            //return new Animal();
            if ("Dog".equals(classType)) {
                return new Dog();
            } else if ("Cat".equals(classType)) {
                return new Cat();
            } else if ("Animal".equals(classType)) {
                return new Animal();
            } else {
                return null;
            }
        }
    
    }
    
    

3.6 多态的好处和弊端

  • 好处:

    • 可以将方法的参数定义为父类引用,使程序编写的更简单,提高程序的灵活性,扩展性
    • 案例参考多态的应用场景
  • 弊端:

    • 无法直接访问子类独有的成员变量和成员方法(因为多态时成员访问特点编译都是看左边(父类))

    • 案例:

      class Animal {
          public void eat() {
              System.out.println("吃东西...");
          }
      }
      
      class Dog extends Animal {
          // 独有的成员变量
          int num = 10;
      
          @Override
          public void eat() {
              System.out.println("狗吃骨头...");
          }
      
          // 独有的成员方法
          public void lookHome() {
              System.out.println("狗正在看家...");
          }
      }
      
      public class Test {
          public static void main(String[] args) {
              // 多态的弊端: 无法访问子类独有的成员变量和成员方法
              // 变量多态
              Animal anl1 = new Dog();
              // System.out.println(anl1.num);// 编译报错,因为编译看左边,父类没有num成员变量
              // anl1.lookHome();// 编译报错,因为编译看左边,父类没有lookHome成员方法
              
              // 返回值多态
              Animal anl2 = method1("Dog");
              // System.out.println(anl2.num);// 编译报错,因为编译看左边,父类没有num成员变量
              // anl2.lookHome();// 编译报错,因为编译看左边,父类没有lookHome成员方法
          }
      
          // 形参多态
          public static void method(Animal anl) {
              anl.eat();
              // anl.lookHome();// 编译报错,因为编译看左边,父类没有lookHome成员方法
          }
      
          // 返回值多态
          public static Animal method1(String classType) {
              //return new Dog();
              //return new Cat();
              //return new Animal();
              if ("Dog".equals(classType)) {
                  return new Dog();
              } else if ("Animal".equals(classType)) {
                  return new Animal();
              } else {
                  return null;
              }
          }
          
      }
      

3.7 引用类型转换

  • 为什么要转型

    • 为了实现多态
    • 为了解决多态的弊端
  • 如何转型:

    • 向上转型: 父类类型 变量名 = new 子类名(实参);
    • 向下转型: 子类类型 变量名 = (子类类型) 父类类型的变量;
      • 注意: 父类类型的变量指向的对象一定是左边子类类型的对象,否则会报类型转换异常ClassCastException
  • 避免转型异常—>转型判断

    • instanceof关键字
    • 格式: 变量名 instanceof 数据类型
    • 执行:
      • 如果变量指向的对象的数据类型是属于后面的数据类型,就返回true,否则返回false
  • 案例:

    class Animal {
        public void eat() {
            System.out.println("吃东西...");
        }
    }
    
    class Dog extends Animal {
        // 独有的成员变量
        int num = 10;
    
        @Override
        public void eat() {
            System.out.println("狗吃骨头...");
        }
    
        // 独有的成员方法
        public void lookHome() {
            System.out.println("狗正在看家...");
        }
    }
    
    class Cat extends Animal {
        @Override
        public void eat() {
            System.out.println("猫吃鱼...");
        }
    
    }
    
    public class Test1 {
        public static void main(String[] args) {
            /*
                为什么转型
                    1.为了实现多态
                    2.为了解决多态的弊端(无法访问子类独有的成员变量和成员方法)
                引用类型转换分类
                  向上转型:子类类型向父类类型转换的过程,称为向上转型。这个过程是默认的。
                           格式:父类类型  变量名 = new 子类类型()  或 子类对象引用;
                            Dog d = new Dog();
                            Animal anl = new Dog();
                            Animal anl = d;
                  向下转型:父类类型向子类类型转换的过程,称为向下转型,这个过程需要强制执行。
                        格式:子类类型 变量名 = (子类类型) 父类类型变量;
                        要求: 父类类型的变量必须指向的是前面子类类型的对象
    
                  类型转换判断:  使用instanceof关键字
                       格式: 变量名 instanceof 数据类型
                       执行:
                            如果变量指向的对象是属于后面的数据类型,那就返回true
                            如果变量指向的对象不属于后面的数据类型,那就返回false
    
            */
            // 向上转型: 父类类型的引用指向子类类型的对象
            Animal anl1 = new Dog();
            anl1.eat();
            // System.out.println(anl1.num);// 编译报错
            // anl1.lookHome();// 编译报错
    
            //Dog d = new Dog();
            //Animal anl2 = d;
    
            System.out.println("=======");
            // 改变anl1指向的对象
            anl1 = new Cat();
    
            // 向下转型: 子类类型向父类类型转换的过程
            if (anl1 instanceof Dog){
                Dog dog = (Dog) anl1;
                System.out.println(dog.num);
                dog.lookHome();
            }
        }
    }
    
    

第四章 内部类

4.1 内部类的概述

  • 概述: 将一个类定义在另一个类的里面,里面的那个类就叫做内部类,外面的那个类就叫做外部类
  • 特点: 内部类是一个独立的类,在编译后,有自己独立的class文件,前面冠以:外部类名+$+内部类类名
  • 分类:
    • 成员内部类
    • 匿名内部类
    • 局部内部类(自己了解)

4.2 成员内部类

  • 概述: 定义在类的成员位置(类中方法外)的类就叫做成员内部类

  • 格式:

    class 外部类名{
        class 内部类名{
            
        }
    }
    
  • 成员访问特点:

    • 成员内部类中的成员变量和成员方法在其他类中访问,需要创建成员内部类对象

    • 格式: 外部类名.内部类名 对象名 = new 外部类名().new 内部类名();

    • 案例:

      public class Body {
      
          public class Heart{
              // 成员变量
              int numN = 20;
      
              // 成员方法
              public void methodN1(){
                  System.out.println("成员内部类的methodN1方法...");
              }
          }
      
      }
      
      public class Test {
          public static void main(String[] args) {
              // 创建Heart对象
              Body.Heart bh = new Body().new Heart();
              System.out.println(bh.numN);
              bh.methodN1();
          }
      }
      
  • 注意事项:

    • 在成员内部类中,可以直接访问外部类的一切成员,包括外部类的私有成员

    • 在外部类中,需要直接访问内部类的成员,需要创建内部类对象来访问

      public class Body {
          int numW = 10;
      
          public void methodW1(){
              System.out.println("外部类的methodW1方法...");
          }
      
          // - 在外部类中,需要直接访问内部类的成员,需要创建内部类对象来访问
          public void methodW2(){
              System.out.println("外部类的methodW2方法...");
      
              // 创建内部类对象
              Heart heart = new Heart();
              System.out.println(heart.numN);
              heart.methodN1();
          }
      
      
          public class Heart{
              // 成员变量
              int numN = 20;
      
              // 成员方法
              public void methodN1(){
                  System.out.println("成员内部类的methodN1方法...");
              }
      
              // - 在成员内部类中,可以直接访问外部类的一切成员,包括外部类的私有成员
              public void methodN2(){
                  System.out.println(numW);
                  methodW1();
              }
          }
      }
      
      public class Test {
          public static void main(String[] args) {
              // 创建Heart对象
              Body.Heart bh = new Body().new Heart();
              System.out.println(bh.numN);
              bh.methodN1();
              bh.methodN2();
      
              System.out.println("======");
              new Body().methodW2();
          }
      }
      
      
  • 扩展

     class Body {
        int num = 10;
    
        public class Heart{
            // 成员变量
            int num = 20;
    
            // 成员方法
            public void show(){
                int num = 30;
                System.out.println("局部变量num:"+num);// 30
                System.out.println("内部类的成员变量num:"+this.num);// 20
                System.out.println("外部类的成员变量num:"+Body.this.num);// 10
            }
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 创建Heart对象
            Body.Heart bh = new Body().new Heart();
            bh.show();
        }
    }
    
    

4.3 匿名内部类(重点)

  • 概述

    • 本质其实就是一个类的子类对象
    • 本质其实就是一个接口的实现类对象
  • 格式

    new 类名(){
        必须重写所有抽象方法
    };
    
    new 接口名(){
        必须重写所有抽象方法
    };
    
  • 使用场景

    • 如果想得到一个抽象类的子类对象,那么就可以直接给该类的匿名内部类
    • 如果想得到一个接口的实现类对象,那么就可以直接给该接口的匿名内部类
  • 案例1:

    abstract class Animal{
        public abstract void eat();
    }
    
    public class Test {
        public static void main(String[] args) {
            // 需求:调用Animal类的eat方法
            /*
                以前:
                    1.创建一个子类继承Animal类,在子类中重写eat方法
                    2.创建子类对象
                    3.使用子类对象调用eat方法
    
                现在:
                    直接创建Animal类的匿名内部类,然后使用该匿名内部类调用eat方法即可
             */
            // 父类的引用指向子类的对象
            Animal anl1 = new Animal() {
                @Override
                public void eat() {
                    System.out.println("匿名内部类的eat...");
                }
            };
            anl1.eat();
        }
    }
    
  • 案例2:

    interface IA{
        void show();
    }
    
    public class Test {
        public static void main(String[] args) {
            // 需求:得到IA接口的实现类对象调用show方法
            /*
                以前:
                    1.创建一个实现类实现IA接口,重写show方法
                    2.创建实现类对象
                    3.使用实现类对象调用show方法
                现在:
                    直接创建IA接口的匿名内部类调用show方法
             */
            IA a = new IA() {
                @Override
                public void show() {
                    System.out.println("匿名内部类的show");
                }
            };
    
            a.show();
    
            System.out.println("=====");
            
            new IA() {
                @Override
                public void show() {
                    System.out.println("匿名内部类的show");
                }
            }.show();
        }
    }
    
    

第五章 引用类型使用小结

5.1 引用类型作为方法参数和返回值
  • 引用类型作为方法的参数传递的是地址值

  • 引用类型作为方法的返回值返回的是地址值

  • 案例

    class Person{
        String name;
    
        public Person(String name) {
            this.name = name;
        }
    
        public void eat(){
            System.out.println("吃饭...");
        }
    }
    public class Test {
        public static void main(String[] args) {
            /*
                需求:定义如下类,演示引用类型作为方法的参数和返回值
                人类:
                    属性:姓名
                    行为:吃饭
                在测试类中,定义一个方法,能接收人类对象,并且可以返回一个人类对象
    
             */
            Person person = new Person("itcast");
            System.out.println(person.name);// itcast
            // 调用method1方法,传入person对象
            Person p1 = method1(person);
            System.out.println(person.name);// itheima
            System.out.println(p1 == person);// true
        }
    
        // 引用类型作为方法的参数和返回值
        public static Person method1(Person p){
            p.name = "itheima";
            return p;
        }
    }
    
    

在这里插入图片描述

5.2 引用类型作为成员变量
abstract class Pet{}

interface FaShu{}

class Person{
    int age;// 基本类型作为成员变量
    String name; // 普通类作为成员变量
    Pet pet;// 抽象类作为成员变量
    FaShu fs;// 接口作为成员变量
}

public class Test {
    public static void main(String[] args) {
        // 创建Person对象
        Person p = new Person();
        // 给p对象的属性赋值
        p.age = 18;
        p.name = "张三";
        p.pet = new Pet() {};// 成员变量类型为抽象类,那么就赋该抽象类的子类对象
        p.fs = new FaShu() {};// 成员变量类型为接口,那么就赋该接口的实现类对象
    }
}

总结

必须练习:
	1.static修饰成员变量的格式和使用
    2.static修饰成员方法的格式和使用
    3.static修饰代码块的格式和使用 
    4.定义接口,实现接口,接口中成员的访问特点    
    5.实现多态
    6.多态场景下成员访问特点
    7.多态的应用场景---->形参多态,返回值多态  重点重点
    8.解决多态的弊端--->向下转型,转型判断
    9.匿名内部类----> 本质,格式
        
- 能够使用static修饰成员变量
    格式: static 数据类型 变量名
    使用: 类名.变量名;
    特点: 被该类的所有对象共享
        
- 能够使用static修饰成员方法
    格式: 方法的返回值类型前面加static
    使用: 类名.方法名(实参);

- 能够使用static修饰静态代码块   
    格式: static{}
	位置: 类中方法外
	执行: 随着类的加载而执行,并且只执行一次,优先于构造方法执行
        
- 能够写出接口的定义格式
    格式: public interface 接口名{}

- 能够写出接口的实现格式
   格式: 单实现,多实现,先继承后实现
       public class 实现类名 implements 接口名{}
       public class 实现类名 implements 接口名1,接口名,...{}
       public class 实现类名 extends 父类 implements 接口名1,接口名,...{}

- 能够说出接口中的成员特点
    接口中的常量: 主要供接口名直接访问,当然也可以被实现类继承
    接口中的抽象方法: 就是供实现类重写的
    接口中的默认方法: 可以供实现类对象直接继承调用,也可以供实现类重写
    接口中的静态方法: 只能供接口名直接访问,不能别实现类继承
    接口中的私有方法: 只能在接口内部直接访问,不能被实现类继承
        
- 能够说出多态的前提
    继承\实现
    父类的引用指向子类的对象\接口的引用指向实现类的对象
    方法的重写
        
- 能够写出多态的格式
   父类类型 变量名 = new 子类类型(实参);

- 能够理解多态向上转型和向下转型  
    向上转型: 父类类型 变量名 = new 子类类型(实参);   存在的意义就是为了实现多态
    向下转型: 子类类型 变量名 = (子类类型)父类类型的变量; 存在的意义就是为了解决多态的弊端
             注意: 父类类型的变量一定要指向左边子类类型的对象,否则会报类型转换异常ClassCastException
    避免转型异常; 变量名 instanceof 数据类型
        
- 能够说出内部类概念      
    一个类中包含另一个类,被包含的类就是内部类
        
- 能够理解匿名内部类的编写格式
     本质: 表示一个类的子类对象,或者一个接口的实现类对象
     格式:
			new 类名(){重写抽象方法};
            new 接口名(){重写抽象方法};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值