106 阅读9分钟

7.3枚举类型

1.什么是枚举?jdk1.5
    枚举在Java中是一种特殊的类,
  特殊:这个类型的对象是固定的有限的几个常量对象。
    
2.案例:
    星期week,这个类,它的对象就可以限定在7个。
​
3.如何声明枚举类?
 (1)旧的,JDK1.5之前
    A:构造器私有化
    B:在类的内部预先创建好n个对象供外界使用
    public static final 数据类型 常量对象名 = new 数据类型(【实参列表】);
 (2)新的,JDK1.5之后(更推荐)
    【修饰符】 enum 枚举类型{
    常量对象名,常量对象名(),常量对象名(【实参列表】);
    // 如果只写常量对象名或常量对象名后面写空(),都是表示用无参构造
    // 如果常量对象名后面加(实参列表),表示有参构造
        其他成员
}
​
4、枚举类型特点和要求
    (1)枚举类型的构造器一定private
    (2)枚举类型中需要预先创建好n个常量对象
    (3)使用enum声明的枚举类型,常量对象的指定(本质就是创建)必须在枚举类中的首行。
 如果常量对象列表后面没有其他成员了, ; 可以省略
 如果常量列表后面还有其他成员, ; 不能省略
    (4)使用enum声明的枚举类型,默认的父类是java.lang.Enum类,
 当然Enum的父类仍然是Object。而且枚举类型不能再继承别的类了,
 也就是说,他的直接父类只能是Enum类。
    (5)因为构造器私有化的问题,枚举类没有子类。
 5、枚举类的方法
  总:除了Object类的方法,也会继承Enum类的方法,
    (1)Enum类已经重写过一次toString,当然我们自己的枚举类还可以继续重写。
    (2)其他方法
   String name()
    int ordinal()
    static 枚举类型[] values()    ==>API中没有
    static 枚举类型 valueOf(String) ==>API中没有
    ...
  6switch-case语句
switch(表达式)的类型:byte,short,int,char,String,枚举
​
switch(表达式){
 case 常量名:
    ...
    【break;】
  case 常量名:
      ...
      【break;】
}  
public class Week {
    //快捷键:Ctrl + Shift + U,大小写转换
    public static final Week MONDAY = new Week("星期一");
    public static final Week TUESDAY = new Week("星期二");
    public final static Week WEDNESDAY = new Week("星期三");
    public final static Week THURSDAY = new Week("星期四");
    public final static Week FRIDAY = new Week("星期五");
    public final static Week SATURDAY = new Week("星期六");
    public final static Week SUNDAY = new Week("星期七");
​
    private String description;
    private Week(){}
​
    private Week(String description) {
        this.description = description;
    }
​
    //....剩下的成员声明和原来的普通类一样
}
public class TestWeek {
    public static void main(String[] args) {
/*        Week[] weeks = new Week[10];
        for (int i = 0; i < weeks.length; i++) {
            weeks[i] = new Week();//报错,因为构造器私有化了,外面无法创建对象
        }*/
​
        Week mon = Week.MONDAY;
        System.out.println(mon);
    }
}
//enum就是代替原来的class
public enum Month {
​
    JANUARY("一月"),FEBRUARY(),MARCH,APRIL,MAY,JUNE,JULY,AUGUST,SEPTEMBER,OCTOBER,NOVEMBER,DECEMBER;
​
    private String description;
​
    Month() {//省略了private
    }
​
    Month(String description) {
        this.description = description;
    }
​
    @Override
    public String toString() {
        return super.toString() + ": " + description;
    }
}
​
import java.util.Scanner;
​
public class TestMonth {
    public static void main(String[] args) {
        Month m = Month.JANUARY;
        System.out.println(m);
​
        String name = m.name();//常量对象名
        int ordinal = m.ordinal();//常量对象的序号、下标
        System.out.println("name = " + name);
        System.out.println("ordinal = " + ordinal);
​
        Month b = Month.FEBRUARY;
        System.out.println("二月的name = " + b.name());
        System.out.println("ordinal = " + b.ordinal());
​
        System.out.println("-----------------------");
        Month[] months = Month.values();
        for (int i = 0; i < months.length; i++) {
            System.out.println(months[i]);
        }
​
        System.out.println("-----------------------");
        Scanner input = new Scanner(System.in);
        System.out.print("请输入你要的月份名称(大写):");
        String monthName = input.next();
        Month month = Month.valueOf(monthName);
        System.out.println("month = " + month);
​
        switch (month){
            case JANUARY:
                System.out.println("一月是一年的第一个月份");break;
            case FEBRUARY:
                System.out.println("二月是龙抬头的月份");break;
                //....
        }
​
        input.close();
    }
}

7.4内部类

回忆:之前学过的
    类的成员:成员变量(静态变量和实例变量)
            成员方法(静态方法、非静态方法、抽象方法、native方法、final方法)
            构造器(无参构造、有参构造)
回忆:
    类的定义:一类具有相同特征的事物的抽象描述。
​
今天:内部类
内部类的分类:
  (1)成员内部类:在类中方法外
  A:静态成员内部类,简称静态内部类
  B:非静态成员内部类,简称成员内部类
  (2)局部内部类:在方法体的内部
  c:有名字的局部内部类
  D:匿名的局部内部类,简称匿名内部类   

7.4.1静态内部类

public class Testpublic class TestStaticInnerClass {//外部类
​
    public static void main(String[] args) {
        //在外部类Outer的外面
        //调用静态内部类的静态方法
        Outer.Inner.inTest();
​
        //调用静态内部类的非静态方法
        Outer.Inner in = new Outer.Inner();
        in.inMethod();
    }
}
​
class Outer{//外部类
    private static int a = 1;
    private static int c;
    private int d;
​
    public static class Inner {//静态内部类
        private static int a = 2;
        private int b;
​
        public void inMethod(){
            System.out.println("静态内部类的非静态方法");
            System.out.println("外部类.a = " + Outer.a);
            System.out.println("a = " + a);
            System.out.println("c = " + c);
//            System.out.println("d = " + d);//错误
        }
​
        public static void inTest(){
            System.out.println("静态内部类的静态方法");
        }
    }
​
/*    public static class In{//另一个静态内部类
        private static int a;
        private int b;
    }*/
​
    public static void outMethod(){
        System.out.println("静态内部类.a = " + Inner.a);
        Inner inner = new Inner();
        System.out.println("静态内部类.b = " + inner.b);
    }
}
​

7.4.2 非静态内部类

1、非静态内部类的声明格式
   【修饰符】 class 外部类{
       【其他修饰符】 class 非静态内部类{
           
       }
   } 
2、从类的角度
    (1)是否有自己独立的字节码文件?
    外部类名$非静态内部类名.class
    
    (2)是否可以继承自己的父类,实现自己的父接口?
        该父类和父接口与外部类无关。
        
        答案:可以
      
    (3)是否可以有自己的成员
        答案:可以
        
     注意:非静态内部类,在JDK16之前,不允许声明自己的静态成员  
        成员变量(实例变量)
成员方法(非静态方法、抽象方法、native方法、final方法)
构造器(无参构造、有参构造)
成员内部类(但是,不会这么干)
​
    (4)修饰符
     外部类的修饰符:public或缺省,final,abstract
成员内部类的修饰符:(多)
权限修饰符:public,protected,缺省,private
其他修饰符:final,abstract3、从成员的角度
(1)在非静态内部类中使用外部类的静态成员,包括私有的。
    可以,直接用
(2)在非静态内部类中使用外部类的非静态成员,包括私有的。
    可以,直接用
(3)在外部类中使用非静态内部类的静态成员,包括私有的。(无)
(4)在外部类中使用非静态内部类的非静态成员,包括私有的。
    可以,需要通过"非静态内部类的对象名."进行访问
(5)如果非静态内部类不是私有的,在外部类的“外面”是否可以使用?
A:在外面使用非静态内部类的静态成员(无)
B:在外面使用非静态内部类的非静态成员
    外部类名 out = new 外部类名(【实参列表】);
    外部类名.非静态内部类名 in = out.new 非静态内部类名(【实参列表】);
    in.非静态成员
​
    另一种写法:
        在外部类中声明一个方法,用于返回内部类的对象。
        外部类名 out = new 外部类名(【实参列表】);
        外部类名.非静态内部类名 in = out.get内部类();
        in.非静态成员
​
(6)当非静态内部类中的成员与外部类的静态成员重名了,
    使用"外部类名.静态成员"表示的是外部类的,
    不加"."的表示自己的。
 (7)当非静态内部类中的成员与外部类的非静态成员重名了,
     使用"外部类名.this.非静态成员"表示的是外部类的,
     不加"外部类名.this."的表示自己的。
            
4、什么情况下要使用静态成员内部类,
什么情况下,只能使用非静态成员内部类?
​
如果在成员内部类中,需要访问外部类的非静态成员,
那么成员内部类就只能是非静态成员内部类。
​
如果在成员内部类中,不需要访问外部类的非静态成员,
那么成员内部类就可以是静态成员内部类。            
public class TestNonStaticInnerClass {//外部类
​
    public static void main(String[] args) {
        //在外部类Outer的外面
        //调用Inner的inMethod方法
        Outer out = new Outer();
//        Outer.Inner in = new Outer.Inner();//错误,因为Inner是Outer的非静态成员,依赖于Outer的对象
        Outer.Inner in = out.new Inner();//依赖于外部类的对象,才能使用非静态成员内部类的构造器
        in.inMethod();
​
        Outer.Inner in2 = out.getInner();
        in2.inMethod();
    }
}
​
class Outer{//外部类
    private static int a=1;
    private int b=1;
    private static int c;
    private int d;
​
    public class Inner {//非静态内部类
        private int a = 2;
        private int b = 2;
​
        public void inMethod(){
            System.out.println("非静态内部类的非静态方法");
            System.out.println("外部类的a = " + Outer.a);
            System.out.println("外部类的b = " + Outer.this.b);
            System.out.println("a = " + a);
            System.out.println("b = " + b);
            System.out.println("c = " + c);
            System.out.println("d = " + d);
        }
    }
​
​
    public void outMethod(){
        Inner in = new Inner();
        System.out.println("非静态内部类.a = " + in.a);
        System.out.println("非静态内部类.b = " + in.b);
    }
​
    public Inner getInner(){
        return new Inner();
    }
}

7.4.3 局部内部类

1、局部内部类的声明方式:
【修饰符】 class  外部类{
    【修饰符】 返回值类型 方法名(【形参列表】){
        【其他修饰符】 class 局部内部类{
​
        }
    }
}
​
​
2、从类的角度
  (1)是否有自己独立的字节码文件?
  外部类名$编号局部内部类名.class
​
  同一个外部类中,不同的方法内部可能存在同名的局部内部类。
​
  (2)是否可以继承自己的父类,实现自己的父接口?
  该父类和父接口与外部类无关。
​
  答案:可以
​
  (3)是否可以有自己的成员
  答案:可以
​
  注意:局部内部类,在JDK16之前,不允许声明自己的静态成员
​
  成员变量(实例变量)
  成员方法(非静态方法、抽象方法、native方法、final方法)
  构造器(无参构造、有参构造)
  成员内部类(但是,不会这么干)
​
  (4)修饰符
  局部内部类的修饰符:final,abstract3、从成员的角度
(1)在局部内部类中使用外部类的静态成员,包括私有的。
    可以,直接用
(2)在局部内部类中使用外部类的非静态成员,包括私有的。
    取决于所在的方法。所在方法是非静态的,就可以直接使用。
(3)在外部类中使用局部内部类的静态成员,包括私有的。(无)
(4)在外部类中使用局部内部类的非静态成员,包括私有的。
        严格遵守作用域。仅限于当前方法,而且要在声明之后。
​
(5)如果“局部内部类”想要在外部类的“外面”使用,是否可以?(不可以)
​
(6)当局部内部类中的成员与外部类的静态成员重名了,
    使用"外部类名.静态成员"表示的是外部类的,
    不加"外部类名."的表示自己的。
 (7)当局部内部类中的成员与外部类的非静态成员重名了,
     使用"外部类名.this.非静态成员"表示的是外部类的,
     不加"外部类名.this."的表示自己的。
​
(8)在局部内部类中可以使用当前方法的局部变量,但是该局部变量只能是final的。
JDK1.8之前,必须手动加final,JDK1.8之后,一旦局部变量被局部内部类使用,会自动加final
​
为什么局部变量必须是final的,才能被局部内部类使用?(冷门)
原因:
    因为局部变量的生命周期是非常短。当前方法运行结束,
    局部变量的内存就随着方法出栈自动就消失了。
    为了解决这个问题,编译器会在局部内部类中自动添加一个成员变量,
    用于存储该局部变量的值。
​
    因为这个操作是编译器自动完成的。
    大部分人会以为它们就是同一个变量,值应该始终保持一致。
    为了避免“误解”,干脆就规定它们必须是final,值不可以修改。
    当初什么值,就是什么值。
package com.atguigu.inner.local;
​
public class TestLocalInner {
    public static void main(String[] args) {
        Father obj = Outer.outMethod();
        /*
        obj的编译时类型是Father类型,
            运行时类型是Inner
         */
        obj.inMethod();
    }
}
​
abstract class Father{
    public abstract void inMethod();
}
class Outer{
    private static int a;
    private int b;
​
    public static Father outMethod(){//静态方法
       final int num = 1;
​
        class Inner extends Father{//局部内部类
            private int a;
            private int b;
​
            public  void inMethod(){
                 System.out.println("外部类.a = " + Outer.a);
//                 System.out.println("外部类.b = " + Outer.this.b);//没有外部类的对象,静态方法
                 System.out.println("a = " + a);
//                 System.out.println("b = " + b);//静态方法不能使用本类的非静态成员
​
                 System.out.println("num = " + num);
             }
        }
​
        /*Inner in1 = new Inner();
        in1.inMethod();
​
        Inner in2 = new Inner();
        in2.inMethod();*/
​
        return new Inner();
    }
​
    public void outTest(){//非静态方法
        class Nei{//局部内部类
            private int a;
            private int b;
​
            public  void inMethod(){
                System.out.println("外部类.a = " + Outer.a);
                System.out.println("外部类.b = " + Outer.this.b);
                System.out.println("a = " + a);
                System.out.println("b = " + b);
            }
        }
    }
}
​

7.4.4 匿名内部类

1、匿名内部类
首先是一个局部内部类,所有局部内部类的特点它都有。
其次它没有名字,一次性的。
​
通常匿名内部类不会很复杂,里面通常只有1个或2个方法,
而且该方法一般都是重写父类或父接口的方法,
很少自定义自己扩展的方法。
​
2、语法
(1)new 父类(){
​
}
​
(2)new 父接口(){
​
}
​
(3)new 父类(实参列表){
​
}
​
因为匿名内部类是一次性,没有名字,所以必须在类声明的同时,把它唯一的对象创建好。
又因为他没有名字,需要写父类或父接口的名字。
(1)new 父类(){} 的意思是匿名内部类的构造器首行默认调用父类的“无参”构造。
(3)new 父类(实参列表){} 的意思是匿名内部类的构造器首行默认调用父类的“有参”构造。
(2)new 父接口(){}的意思是匿名内部类的构造器首行默认调用父类的“无参”构造。父类是Object.
同时也指明了该匿名内部类的一个父接口。
​
辨别:
    new Object();   ==> 创建Object类本类的对象
    new Object(){}; ==> 创建Object的一个匿名子类的对象
​
3、匿名内部类调用方法的格式
(1)new 父类/父接口(【构造器的实参列表】){
​
   }.方法(【方法的实参列表】);
​
(2)父类/父接口 变量名 = new 父类/父接口(【构造器的实参列表】){
​
    };//多态引用
    变量名.方法(【方法的实参列表】);
package com.atguigu.inner.anoy;
​
public class TestInner {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object(){};//匿名子类的对象取了个名字o2
​
        System.out.println(o1.getClass());//class java.lang.Object
        System.out.println(o2.getClass());//class com.atguigu.inner.anoy.TestInner$1
​
        System.out.println("-------------------------");
        new Object(){
            public void m1(){
                System.out.println("Object第二个匿名子类的m1方法");
            }
        }.m1();//匿名子类的匿名对象 调用 m1方法
​
        Object o3 = new Object(){
            //不是重写
            public void m2(){
                System.out.println("Object第三个匿名子类的m2方法");
            }
        };
        //o3.m2();//o3的编译时类型是Object,m2是子类扩展的方法,编译时是无法调用子类扩展的方法
//        ((子类名)o3).m2();//无法编写向下转型的类型,没有名字
​
        System.out.println("-------------------------");
        Father f = new Father(){
            @Override
            public void method() {
                System.out.println("Father的匿名子类重写Father方法");
            }
        };//多态引用
        f.method();//f编译时看Father类,有method方法,编译通过,运行时执行匿名子类重写的method
        f.test();
        //给对象取名,意味着,同一个对象可以被多次使用
​
        System.out.println("-------------------------");
        Mother m = new Mother(100){
            @Override
            public void method() {
                System.out.println("子类重写Mother的方法");
            }
        };
        m.method();
​
        System.out.println("-------------------------");
        Flyable fObject = new Flyable(){
            @Override
            public void fly() {
                System.out.println("实现接口的抽象方法");
            }
        };
        fObject.fly();
        fObject.fly();
        fObject.fly();
    }
}
​
class Father{
    public void method(){
        System.out.println("Father.method");
    }
    public void test(){
        System.out.println("Father.test");
    }
}
class Mother{
    private int num;
​
    public Mother(int num) {
        this.num = num;
    }
    public void method(){
        System.out.println("Mother.method" + num);
    }
}
​
interface Flyable{
    void fly();
}

7.5 this和super

7.5.1 this和super的含义

1、含义
(1this是一个变量,表示当前对象。在构造器和非静态方法,非静态代码块(关于非静态代码块后面学习),非静态内部类中,
都会默认有一个this变量。
A:构造器:表示正在new的那个对象
class Father{
    private int num;
    public Father(){
        System.out.println(this);
    }
​
    public Father(int num){
        this.num = num;
    }
}
class Son extends Father{
    public Son(){
        super();
    }
    public Son(int num){
        super(num);
    }
}
​
①Father f = new Father();  ==> this是Father类对象
②Father f = new Son(); ==> this是Son类的对象
​
好比:
   xx :"我爱你!";
​
B:非静态方法中
this代表正在调用该方法的对象
class Father{
    public void method(){
        System.out.println(this);//this的编译时类型是Father,运行时类型不一定Father类型
    }
}
class Son extends Father{
}
①Father f = new Father();
 f.method();        ==> this是Father类对象
②Father f = new Son();
  f.method();       ==> this是Son类的对象
​
C:非静态内部类
class Outer{
    class Inner{
        public void method(){
             System.out.println(this);//this是Inner类或Inner的子类对象
             System.out.println(Outer.this);//this是Outer类或Outer的子类对象
        }
    }
}
​
(2super不是一个变量,不能独立使用。仅仅是一个关键字而已,用于“复用”父类声明的xx代码。
   super的使用是依赖于当前对象,即当前对象要完成xx事,复用父类的xx代码。

7.5.2 this的用法

2、用法
thisthis的编译时类型是本类类型。
this的运行时类型要看“当前对象”的运行时类型。
​
(1this:独立使用
(2this.成员变量
    当局部变量与成员变量重名了,那么在成员变量前面加"this."
    如果没有重名问题,完全可以省略this.
(3this.成员方法:完全可以省略this.
    编译时:表示调用本类的或父类的未被重写的xx方法。
    运行时:
        this代表的是本类的对象,那么执行本类的xx方法
        this代表的是子类的对象,那么要看子类是否重写了该方法,如果重写了,就执行子类重写的方法。
​
(4this() 或 this(实参列表):调用“本类”的构造器,必须在构造器的首行。
(5)外部类名.this.非静态成员:用于在内部类中访问外部类的非静态成员
​

7.5.3 super的用法

3、super
前提条件:通过super复用的构造器、成员变量、成员方法等都必须在子类可见。
(1)super.成员变量
    当子类声明了和父类重名的成员变量时,可以用“super.成员变量”表示引用父类声明的那个成员变量。
​
    注意:实际开发中要避免子父类声明重名的成员变量。
​
(2)super.成员方法
    当子类“重写”了父类某个方法,子类又想要“复用”父类被重写的方法的方法体,
    就可以使用”super.被重写方法“,否则会出现死循环递归,导致内存溢出。
    要是父类的方法没有被子类重写,完全不需要加"super."。
​
(3)super()和super(实参列表):在子类的构造器首行,必须调用父类的构造器。
    为什么要调用父类的构造器?复用父类构造器的代码为 从父类继承的成员变量进行初始化。
​
​
this() 或 this(实参列表)与 super()和super(实参列表)只能四选一。
如果都没写,默认是super();
​
(4)父接口名.super.默认方法名:解决冲突问题

7.5.4 变量的访问原则

4、变量的访问
(1)变量的前面如果既没有this.,也没有super.的时候,如果辨别它访问的是谁?
A:先看局部变量
B:再看本类的成员变量
C:再看父类的成员变量
从A->B->C,找到为止。
​
(2)变量的前面有this.的时候,如果辨别它访问的是谁?
B:先看本类的成员变量
C:再看父类的成员变量
从B->C,找到为止。
​
(3)变量的前面有super.的时候,如果辨别它访问的是谁?
C:直接看父类的成员变量
package com.atguigu.keyword;
​
public class TestVariable {
    public static void main(String[] args){
        Zi son = new Zi();
        son.test();
        son.method(30,13);
    }
}
class Fu{
    int a = 10;
    int b = 11;
}
class Zi extends Fu{
    int a = 20;
​
    public void test(){
        //子类与父类的属性同名,子类对象中就有两个a
        System.out.println("子类的a:" + a);//20  先找局部变量找,没有再从本类成员变量找
        System.out.println("子类的a:" + this.a);//20   先从本类成员变量找
        System.out.println("父类的a:" + super.a);//10    直接从父类成员变量找
​
        //子类与父类的属性不同名,是同一个b
        System.out.println("b = " + b);//11  先找局部变量找,没有再从本类成员变量找,没有再从父类找
        System.out.println("b = " + this.b);//11   先从本类成员变量找,没有再从父类找
        System.out.println("b = " + super.b);//11  直接从父类局部变量找
    }
​
    public void method(int a, int b){
        //子类与父类的属性同名,子类对象中就有两个成员变量a,此时方法中还有一个局部变量a
        System.out.println("局部变量的a:" + a);//30  先找局部变量
        System.out.println("子类的a:" + this.a);//20  先从本类成员变量找
        System.out.println("父类的a:" + super.a);//10  直接从父类成员变量找
​
        System.out.println("局部变量的b = " + b);//13  先找局部变量
        System.out.println("b = " + this.b);//11  先从本类成员变量找
        System.out.println("b = " + super.b);//11  直接从父类局部变量找
    }
}
​

7.5.5 this和super综合面试题

面试题1

package com.atguigu.exam1;
​
public class TestOne {
    public static void main(String[] args) {
        Son s = new Son();
        s.test();
​
        System.out.println("----------------");
        Daughter d = new Daughter();
        d.test();
    }
}
​
class Father {
    protected int num = 10;
​
    public int getNum() {
        return num;//等价于this.num,此处this的编译时类型是Father
    }
}
​
class Son extends Father {
    private int num = 20;
​
    public void test() {
        System.out.println(getNum());//10  本类没有匹配的,去父类匹配,匹配后再看是否有重写
        System.out.println(this.getNum());//10  本类没有匹配的,去父类匹配,匹配后再看是否有重写
        System.out.println(super.getNum());//10  直接去父类匹配,并执行父类的
    }
}
​
class Daughter extends Father {
    private int num = 20;
​
    @Override
    public int getNum() {
        return num;//等价于this.num,此处this的编译时类型是Daughter
    }
​
    public void test() {
        System.out.println(getNum());//20  先在本类匹配
        System.out.println(this.getNum());//20  先在本类匹配
        System.out.println(super.getNum());//10  直接去父类匹配,并执行父类的
    }
}