[JavaSE] 第五章 面向对象(下)

5.1 继承性

  • 继承性的好处:

    1. 减少了代码的冗余,提高了代码的复用性
    2. 便于功能的扩展
    3. 为多态性的使用,提供了前提
  • 继承性的格式:class A extends B {}

    • A:子类、派生类、subclass

    • B:父类、超类、基类、superclass

体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的结构:属性、方法。特别的,父类中声明为 private 的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只是因为封装性的影响,使得子类不能直接调用父类的结构而已。

子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展
子类和父类的关系,不同于子集和集合的关系

  • Java 中继承的规定
    1. 一个类只能有一个父类
    2. 一个类可以被多个子类继承
    3. 子父类是相对的概念
    4. 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
    5. 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法

1.如果我们没有显式的声明一个类的父类的话,则此类继承于 java.lang.Object 类

2.所有的 java 类(出 java.lang.Object 类之外)都直接或间接的继承于 java.lang.Object 类

3.意味着,所有的 java 类具有 java.lang.Object 类声明的功能

  • 代码体现

    class Animal {
        String name; //定义name属性
        //定义动物叫的方法
        void shout() {
            System.out.println("动物叫");
        }
    }
    //定义 Dog 类继承 Animal 类
    class Dog extends Animal {
        public void printName() {
            System.out.println("name=" + name);
        }
    }
    

5.2 方法的重写

5.2.1 重写的定义

子类继承父类以后,对父类中同名同参数的方法,进行覆盖操作

重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数
的方法时,实际执行的是子类重写父类的方法

5.2.2 要求

  • 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表

  • 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型

    • 父类被重写的方法的返回值类型是 void,则子类重写的方法的返回值类型只能是 void
    • 父类被重写的方法的返回值类型是 A 类型,则子类重写的方法的返回值可以是 A 类型或 A 类的子类
    • 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
  • 子类的重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符

    • 特殊情况:子类不能重写父类中声明为 private 权限的方法
  • 子类方法抛出的异常不能大于父类被重写方法的异常

子类和父类中的同名同参数的方法要么都声明为非 static 的(考虑重写),要么都声明为 static 的(不是重写)因为 static 方法是属于类的,子类无法覆盖父类的方法。

5.2.3 代码体现

class Animal {
    String name; //定义name属性
    //定义动物叫的方法
    void shout() {
        System.out.println("动物叫");
    }
}
//定义 Dog 类继承 Animal 类
class Dog extends Animal {
    void shout() {
        System.out.println("狗叫");
    }
}

5.3 关键字:super

super 理解为:父类的
super 可以用来调用:属性、方法和构造器

5.3.1 super 的使用

  • 可以在子类的方法或构造器中。通过使用 "super.属性" 或 "super.方法" 的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略 "super."
  • 特殊情况:当子类和父类中定义了同名的属性时,我们想要在子类中调用父类中声明的属性,则必须显式的使用 "super.属性" 的方式,表明调用的是父类中的属性
  • 特殊情况:当子类重写了父类中方法以后,我们想要在子类的方法中调用父类中被重写的方法时,则必须显式的使用 "super.方法" 的方式,表明调用的是父类中被重写的方法

5.3.2 super 调用构造器

  • 可以在子类的构造器中显式的使用 "super(形参列表)" 的方式,调用父类中声明的指定的构造器

  • "super(形参列表)" 的使用,必须声明在子类构造器的首行

  • 在类的构造器中,针对于 "this(形参列表)" 或 "super(形参列表)" 只能二选一,不能同时出现

  • 在构造器的首行,没有显式的声明 "this(形参列表)" 或 "super(形参列表)",则默认调用的是父类中空参的构造方法

  • 在类的多个构造器中,至少有一个类的构造器中使用了 "super(形参列表)" ,调用父类中的构造器

5.3.3 this 和 super 的区别

区别点 this super
访问属性 访问本类中的属性,如果本类没有此属性则从父类中继续查找 直接访问父类中的属性
调用方法 访问本类中的方法,如果本类没有此方法则从父类中继续查找 直接访问父类中的方法
调用构造器 调用本类中构造器,必须放在构造器的首行 调用父类构造器,必须放在子类构造器的首行

5.3.4 代码体现

public class SuperTest {

    public static void main(String[] args) {
        Dog dog = new Dog();
        Dog dog1 = new Dog("");
    }
}

class Animal {
    public Animal(String name) {
        System.out.println("我是一只" + name);
    }
}

class Dog extends Animal {
    public Dog() {
        super("沙皮狗");
        System.out.println("ddd");
    }
    public Dog(String name) {
        this();
    }
}

/* 运行结果:
我是一只沙皮狗
ddd
我是一只沙皮狗
ddd
 */

5.4 子类对象实例化过程

  • 从结果上来看:(继承性)
    • 子类继承父类以后,就获取了父类中声明的属性或方法。创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
  • 从过程上来看:
    • 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了 java.lang.Object 类中空参的构造器为止。正因为加载过所有的父类的结构,所有才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用

明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为 new 的子类对象

5.5 多态性

  • 理解多态性:可以理解为一个事物的多种形态;实现代码的通用性
  • 对象的多态性:父类的引用指向子类的对象(子类的对象赋给父类的引用)
  • 多态的使用:虚拟方法调用
  • 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际
    执行的是子类重写父类的方法。总结:编译看左边,运行看右边
  • 多态性的使用前提:
    • 类的继承关系
    • 方法的重写
  • 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.eat();

        Man man = new Man();
        man.eat();
        man.age = 25;
        man.earnMoney();

        //*************************************//
        System.out.println("**********************");
        //对象的多态性:父类的引用指向子类的对象
        Person p2 = new Man();
        //多态的使用:当调用子类同名同参数的方法时,实际执行的是子类重写父类的方法,虚拟方法调用
        p2.eat();
        p2.walk();
        System.out.println(p2.id);//1001
        //不能调用子类所特有的方法、属性,编译时,p2 是 Person 类型
        ///p2.earnMoney();
//        p2.isSmoking = true;
        //有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,
        //但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。
        //子类特有的属性和方法不能调用

        //如何才能调用子类特有的属性和方法
        //向下转型:使用强制类型转换符
        Man m1 = (Man) p2;
//        m1.earnMoney();
        m1.isSmoking = true;

        //使用强转时,可能出现 ClassCastException 的异常
        //Woman w1 = (Woman) p2;
        //w1.goShopping();

        /*
        instanceof 关键字的使用

        a instanceof A:判断对象 a 是否是类 A 的实例。如果是,返回 true:如果不是,返回 false

        使用情景:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行
        instanceof 的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型

        如果 a instanceof A 返回true,则 a instanceof B 也返回true
        其中,类 B 是 类 A 的父类
         */
        if (p2 instanceof Woman) {
            Woman w1 = (Woman) p2;
            w1.goShopping();
            System.out.println("*******Woman***********");
        }

        if (p2 instanceof Man) {
            Man m2 = (Man) p2;
            m2.earnMoney();
            System.out.println("*******Man***********");
        }

        if (p2 instanceof Person) {
            System.out.println("*******Person***********");
        }

        if (p2 instanceof Object) {
            System.out.println("*******Object***********");
        }

        //练习:
        //问题一:编译时通过,运行时不通过
        //举例一:
//        Person p3 = new Woman();
//        Man m3 = (Man) p3;
        //举例二:
//        Person p4 = new Person();
//        Man m4 = (Man) p4;

        //问题二:编译通过,运行时也通过
        Object obj = new Woman();
        Person p = (Person) obj;

        //问题三:编译不通过
//        Woman woman = new Man();

    }
}

class Person {

    String name;
    int age;
    int id = 1001;

    public void eat() {
        System.out.println("人吃饭");
    }

    public void walk() {
        System.out.println("人走路");
    }
}

class Man extends Person {

    boolean isSmoking;
    int id = 1002;

    public void earnMoney() {
        System.out.println("男人负责挣钱");
    }

    public void eat() {
        System.out.println("男人吃饭");
    }
    public void walk() {
        System.out.println("男人走路");
    }
    public void sleep() {
        System.out.println("男人睡觉");
    }
}

class Woman extends Person {

    boolean isBeauty;

    public void goShopping() {
        System.out.println("女人喜欢购物");
    }
    public void eat() {
        System.out.println("女人吃饭");
    }
    public void walk() {
        System.out.println("女人走路");
    }
}

5.6 Object 类的使用

  • java.lang.Object 类

    • Object 类是所有 Java 类的根父类

    • 如果在类的声明中未使用 extends 关键字指明其父类,则默认父类为 java.lang.Object 类

    • Object 类中的功能(属性、方法)就具有通用性

    • Object 类只声明了一个空参构造器

5.6.1 equals 和 ==

  • == 比较符的使用:

    • 可以使用在基本数据类型和引用数据类型变量中
    • 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等(不一定要类型相同)
    • 如果比较的是引用数据类型:比较的是两个对象的地址值,即两个引用是否指向同一个对象实体
    • 补充: == 符号使用时,必须保证符号左右两边的变量类型一致。
  • equals() 方法的使用:

    • 是一个方法,而非运算符

    • 只能适用于引用数据类型

    • Object类中equals()的定义:

      public boolean equals(Object obj) {
      	return (this == obj);
      }
      //Object类中定义的equals()和==的作用是相同的,比较两个对象的地址值是否相同
      
    • 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的实体内容是否相同

    • 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的实体内容是否相同,这时候就需要对Object类中的equals()进行重写

    • 重写的原则:比较两个对象的实体内容是否相同

public class EqualsTest {

    public static void main(String[] args) {

        //基本数据类型
        int i = 10;
        int j = 10;
        double d = 10.0;
        System.out.println(i == j);//true
        System.out.println(i == d);//true
        char c = 10;
        System.out.println(c == i);//true
        char c1 = 'A';
        char c2 = 65;
        System.out.println(c1 == c2);//true

        //引用数据类型
        Order o1 = new Order("Tom", 11);
        Order o2 = new Order("Tom", 11);
        System.out.println(o1 == o2);//false

        String str1 = new String("a1");
        String str2 = new String("a1");
        System.out.println(str1 == str2);//false

        System.out.println("**************equals**************");
        System.out.println(o1.equals(o2));//false --> true
        System.out.println(str1.equals(str2));//true
    }
}

class Order {

    private String name;
    private int age;

    public Order() {
    }
    public Order(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    //重写的原则:比较两个对象的实体内容是否相同
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Order order = (Order) o;
        return age == order.age &&
                Objects.equals(name, order.name);
    }
}

5.6.2 toString 的使用

  • 当我们输出一个对象的引用时,实际上就是调用当前对象的 toString()

  • Object 类中 toString 的定义:

    public String toString() {
    	return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    

5.7 包装类

  • 有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character
public class WrapperTest {

    //String类型 --> 基本数据类型、包装类
    @Test
    public void test5() {
        String str1 = "234";
        Integer i1 = Integer.valueOf(str1);
        System.out.println(i1);

        int i2 = Integer.parseInt(str1);
        System.out.println(i2);
    }

    //基本数据类型、包装类 ---> String类型
    @Test
    public void test4() {
        int num1 = 10;
        String str1 = String.valueOf(num1);
        String str2 = num1 + "";
    }

    /*
    JDK 5.0 新特性:自动装箱和自动拆箱
     */
    @Test
    public void test3() {
        int num = 1;
        method(num);

        //自动装箱:基本数据类型 --> 包装类
        int num2 = 10;
        Integer integer = num2;

        //自动拆箱:包装类 --> 基本数据类型
        Integer integer1 = new Integer(20);
        int num3 = integer1;
    }

    public void method(Object obj) {
        System.out.println(obj);
    }

    //包装类 --> 基本数据类型
    @Test
    public void test2() {
        Integer num1 = 3;

        int i1 = num1.intValue();
        System.out.println(i1);

        int i = Integer.valueOf(num1);
        System.out.println(i);
    }

    //基本数据类型 --> 包装类
    @Test
    public void test1() {
        int num1 = 10;
        Integer integer = num1;
        System.out.println(integer);

        Float f1 = 23.4f;
        System.out.println(f1);

        Boolean b1 = true;
        Boolean b2 = new Boolean("True");
        System.out.println(b2);//true
        Boolean b3 = new Boolean("true34");
        System.out.println(b3);//false

        Order order = new Order();
        System.out.println(order.isMale);//false
        System.out.println(order.isFemale);//null
    }
}

class Order {
    boolean isMale;
    Boolean isFemale;
}
@Test
public void test1() {
    Object o1 = true ? new Integer(1): new Double(2.0);
    System.out.println(o1);//1.0
}

@Test
public void test2() {
    Object o2;
    if (true) {
        o2 = new Integer(1);
    } else {
        o2 = new Double(2.0);
    }
    System.out.println(o2);//1
}

@Test
public void test3() {
    Integer i = new Integer(1);
    Integer j = new Integer(1);
    System.out.println(i == j);//false

    /*
    Integer内部定义了IntegerCache结构,IntegerCache中定义了 integer[],
    保存了从 -128 ~ 127 范围的整数。如果我们使用自动装箱的方式,给 Integer 赋值的
    范围在 -128 ~ 127 范围内时,可以直接使用数组中的元素,不用再去 new 了。目的:提高效率
     */
    Integer m = 1;
    Integer n = 1;
    System.out.println(m == n);//true
    Integer x = 128;
    Integer y = 128;
    System.out.println(x == y);//false
}

5.8 main 方法的语法

  • main() 方法的使用说明:
    • main() 方法作为程序的入口
    • main() 方法也是一个普通的静态方法
    • main() 方法也可以作为我们与控制台交互的方式
public class MainTest {
    public static void main(String[] args) {
        Main.main(new String[100]);
    }
}

class Main {
    public static void main(String[] args) {
        args = new String[100];
        for (int i = 0; i < args.length; i++)  {
            args[i] = "args_" + i;
            System.out.println(args[i]);
        }
    }
}

5.9 关键字:final

  • final 可以用来修饰的结构:类、方法、变量
  • final 用来修饰类:此类不能被其他类所继承
  • final 用来修饰方法:表明此方法不可以被重写
  • final 用来修饰变量:此时的 "变量" 就称为一个常量
    • final 修饰属性:可以考虑赋值的位置有:显示初始化、代码块中赋值、构造器中初始化
    • final 修饰局部变量:
      • 尤其是使用 final 修饰形参时,表明此形参是一个常量。当我们调用此方法时,就给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,且不能重新赋值
  • static final 用来修饰属性:全局常量
public class FinalTest {

    static final int WIDTH = 20;
    final int LEFT;
    final int RIGHT;

    {
        LEFT = 1;
    }

    public FinalTest() {
        RIGHT = 2;
    }

    public FinalTest(int LEFT) {
        this();
    }

    public void doWidth() {
        //Cannot assign a value to final variable 'width'
        //width = 20;
    }

    public void show() {
        final int NUM = 10;//常量
//        NUM += 20;//Cannot assign a value to final variable 'NUM'
    }

    public void show(final int num) {
        System.out.println(num);
//        num += 10;//Cannot assign a value to final variable 'num'
    }

    public static void main(String[] args) {

        FinalTest test = new FinalTest();
        test.show(23);

        int num = 10;

        num = num + 5;
    }
}

final class FinalA {}

//Cannot inherit from final 'com.ws.keyword.FinalA'
//class B extends FinalA {
//
//}

class AA {
    public final void show () {}
}

class BB extends AA {
    //overridden method is final
//    public void show () {
//
//    }
}

5.10 关键字:abstract

  • abstract 可以用来修饰的结构:类、方法

5.10.1 抽象类

  • abstract 修饰类:抽象类
    • 此类不能实例化
    • 抽象类中一定有构造器,便于子类对象实例化时调用(涉及:子类对象实例化的全过程)
    • 开发中,都会抽象类的子类,让子类对象实例化,完成相关的操作

5.10.2 抽象方法

  • abstract 修饰方法:抽象方法
    • 抽象方法只有方法声明,没有方法体
    • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法
    • 若子类重写了父类中的所有的抽象方法后,此子类方可实例化
    • 若子类没有重写父类中所有的抽象方法,则此子类也是一个抽象类,需要使用 abstract 修饰
  • abstract 不能用来修饰:属性、构造器等结构
  • abstract 不能用来修饰私有方法、静态方法、final 的方法、final 的类
public class AbstractTest {
    public static void main(String[] args) {

        //抽象类无法实例化
        //Person person = new Person();
        //person.eat();

        Student student = new Student();
        student.eat();
    }
}

abstract class Creature {
    public abstract void breath();
}

abstract class Person extends Creature {

    String name;
    int age;

    public Person() {}

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

    //不是抽象方法
//    public String eat() {
//
//    }
    //抽象方法
    public abstract void eat();

    //对父类中的抽象方法进行重写,抽象类可以重写父类中的抽象方法,也可以不进行重写,若不进行重写,则继承此类的子类必须对其进行重写
//    public void breath() {
//
//    }

    public void walk() {
        System.out.println("人走路");
    }
}

class Student extends Person {

    public Student() {}

    public Student(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
    }

    @Override
    public void breath() {

    }
}

5.11 接口

  • 接口使用 interface 来定义
  • Java 中,接口和类是并列的两个结构
  • 如何定义接口:定义接口中的成员
    • JDK7 及以前:只能定义全局常量和抽象方法
      • 全局常量:public static final 的,但是书写时,可以省略不写
      • 抽象方法:public abstract
    • JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
  • 接口不能定义构造器,意味着接口不可以实例化
  • Java开发中,接口通过让类去实现(implements)的方式来使用,如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化;如果实现类没有覆盖接口中所有的抽象方法,则此类为抽象类
  • Java 类可以实现多个接口 ---> 弥补了 Java 单继承性的局限性
    • class AA extends BB implements CC, DD, EE
interface Flyable {

    //全局常量
    public static final int MAX_SPEED = 7900;//第一宇宙速度
    int MIN_SPEED = 1;

    //抽象方法
    public abstract void fly();
    //省略了 public abstract
    void stop();

    //接口不能定义构造器
    //public Flyable(){}
    
    //静态方法
    public static void method1() {
        System.out.println("CompareA");
    }

    //默认方法
    public default void method2() {
        System.out.println("默认方法");
    }

    default void method3() {
        System.out.println("默认方法2");
    }
}

5.11.1 接口的使用

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

        Computer computer = new Computer();
        //1.创建了接口的非匿名实现类的非匿名对象
        Flash flash = new Flash();
        computer.transferData(flash);

        //2.创建了接口的非匿名实现类的匿名对象
        computer.transferData(new Printer());

        //3.创建了接口的匿名实现类的非匿名对象
        USB phone = new USB() {
            @Override
            public void start() {
                System.out.println("手机开始工作");
            }

            @Override
            public void stop() {
                System.out.println("手机结束工作");
            }
        };
        computer.transferData(phone);

        //3.创建了接口的匿名实现类的匿名对象
        computer.transferData(new USB() {
            @Override
            public void start() {
                System.out.println("MP3开始工作");
            }

            @Override
            public void stop() {
                System.out.println("MP3结束工作");
            }
        });
    }
}

class Computer {
    public void transferData(USB usb) {// USB usb = new Flash
        usb.start();
        System.out.println("具体传输数据的细节");
        usb.stop();
    }
}

interface USB {

    //常量

    void start();
    void stop();
}

class Flash implements USB {

    @Override
    public void start() {
        System.out.println("U盘开启工作");
    }

    @Override
    public void stop() {
        System.out.println("U盘结束工作");
    }
}

class Printer implements USB {

    @Override
    public void start() {
        System.out.println("打印机开启工作");
    }

    @Override
    public void stop() {
        System.out.println("打印机结束工作");
    }
}

5.11.2 接口中的默认方法

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

        SubClass subClass = new SubClass();

        //知识点1:接口中定义的静态方法,只能通过接口来调用
//        subClass.method1();
        CompareA.method1();

        //知识点2:通过实现类的对象,可以调用接口中的默认方法
//        subClass.method2();
        //如果实现类重写了父类中的默认方法,调用时,仍然调用的是重写以后的方法

        //知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,
        //那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法
        subClass.method3();//SuperClass

        SubClass2 s2 = new SubClass2();
        //知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
        //那么在实现类没有重写此方法的情况下,报错 --> 接口冲突
        //这就需要我们必须在实现类中重写此方法
        s2.method3();
    }
}

class SubClass extends SuperClass implements CompareA, CompareB {

    public void method3() {
        System.out.println("SubClass");
    }

    //知识点5:如何在子类(或实现类)的方法中调用接口中的默认方法
    public void myMethod() {
        method3();//自己定义的重写的方法
        super.method3();//调用的是父类中声明的
        //调用接口中的默认方法
        CompareA.super.method3();
        CompareB.super.method3();
    }
}

class SubClass2 implements CompareA, CompareB {

//    @Override
//    public void method2() {
//
//    }

    @Override
    public void method3() {
        System.out.println("SubClass2-method3");
    }
}

5.12 类的成员之五:内部类

  • Java 中允许将一个类 A 声明在另一个类 B 中,则类 A 就是内部类,类 B 称为外部类
  • 内部类的分类:成员内部类(静态,非静态) VS 局部内部类(方法内、代码块内、构造器内)
  • 成员内部类
    • 一方面:作为外部类的成员:
      • 调用外部类的结构
      • 可以被 static 修饰
      • 可以被 4 种不同的权限修饰
    • 另一方面:作为一个类
      • 类内可以定义属性、方法、构造器等
      • 可以被 final 修饰,表示此类不能被继承(不使用 final 修饰可以被继承)
      • 可以被 abstract 修饰,表示此类不能被实例化
  • 局部内部类
    • 内部类仍然是一个独立的类
    • 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
    • 局部内部类可以使用外部类的成员,包括私有的
    • 局部内部类可以使用外部方法的局部变量,但是必须是 final 的。由局部内部类和局部变量的声明周期不同所致
    • 局部内部类和局部变量地位类似,不能使用 public,protected,缺省,private 修饰
    • 局部内部类不能使用 static 修饰,因此也不能包含静态成员
public class InnerClassTest {
    public static void main(String[] args) {

        //如何实例化成员内部类的对象
        //创建BB实例(静态的成员内部类)
        Person.BB bb = new Person.BB();
        bb.byName();

        //创建BB2实例(非静态的成员内部类)
        //Person.BB2 bb2 = new Person.BB2();//错误
        Person person = new Person();
        Person.BB2 bb2 = person.new BB2();
        bb2.byName();
        bb2.display("形参Name");
        Person.BB2 bb3 = new Person().new BB2();
    }
}

class Person {
    String name = "PersonName";
    int age;
    public void eat() {
        System.out.println("人:吃饭");
    }

    //静态成员内部类
    static class BB {
        String name;//成员变量
        public BB() {}//构造器
        public void byName() {
            System.out.println("方法BB");
            //eat();  //静态不能加载非静态结构
        }
    }

    //非静态成员内部类
    class BB2 {
        String name = "BB2Name";//成员变量
        public BB2() {}//构造器
        public void byName() {
            System.out.println("方法BB2");
            eat();
            Person.this.eat();//完整的调用
        }
        //如何在成员内部类中区分调用外部类的结构
        public void display(String name) {
            System.out.println(name);//方法的形参
            System.out.println(this.name);//调用本类中的name
            System.out.println(Person.this.name);//调用Person类中的name
            System.out.println(age);
        }
    }

    public void method() {
        //局部内部类
        class AA {
            int a = 1;
            int b = Person.this.age;
            String sex = Person.this.sex;
        }
        AA aa = new AA();
        System.out.println(aa.a);
    }

    {
        //局部内部类
        class AA {
        }
    }
}
	//返回一个实现了Comparable接口的类的对象
    public Comparable getComparable1() {
        //创建一个实现了Comparable接口的类
        //方式一:
        class MyComparable implements Comparable {

            @Override
            public int compareTo(Object o) {
                return 0;
            }
        }
        return new MyComparable();
    }

    
    //返回一个实现了Comparable接口的类的对象
    public Comparable getComparable2() {
        //方式二:
        return new Comparable() {
            @Override
            public int compareTo(Object o) {
                return 0;
            }
        };
    }
posted @   Aunean  阅读(35)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示