2021-12-06、12-07

今天简单的复习下Java内部类,面试有的时候问到。

Java内部类

什么是内部类?

内部类就是定义类里面的类。
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。

广泛意义上的内部类一般来说包括这四种:

1、成员内部类
2、局部内部类
3、匿名内部类
4、静态内部类

成员内部类:

成员内部类是最普通的内部类,它的定义为位于另一个类的内部,如下:

/*
*  Circle 是外部类
*/
class Circle {
    double radius = 0;
     // 构造方法
    public Circle(double radius) {
        this.radius = radius;
    }
     
     //  内部类
    class Draw { 
        // 内部类中的方法   
        public void drawSahpe() {
            System.out.println("drawshape");
        }
    }
}

这样看起来,类Draw像是类Circle的一个成员,Circle称为外部类。成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。

class Circle {
    private double radius = 0;
    public static int count =1;
    public Circle(double radius) {
        this.radius = radius;
    }
     
    class Draw {     //内部类
        public void drawSahpe() {
            System.out.println(radius);  //外部类的private成员
            System.out.println(count);   //外部类的静态成员
        }
    }
}

注意:,当成员内部类和外部类成员变量同名或者方法,会发生隐藏现象,默认访问的是成员内部类的成员。如果要访问外部类的同名成员,需要用this关键字。

外部类.this.成员变量
外部类.this.成员方法

外部类想要访问内部类的成员变量或者方法需要创建内部类的对象。

public class Circle {

    private double radius = 0;

    public Circle(double radius) {
        this.radius = radius;
        // 必须先创建成员内部类的对象,再进行访问
        getDrawInstance().drawSahpe();
    }

    // 创建Draw对象
    private Draw getDrawInstance() {
        return new Draw();
    }

    // 内部类
    class Draw {
        public void drawSahpe() {
            // 外部类的private成员
            System.out.println(radius);
        }
    }
}

成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:

public class Test {
    public static void main(String[] args)  {
        //第一种方式:
        Outter outter = new Outter();
         //  必须通过Outter对象来创建
        Outter.Inner inner = outter.new Inner(); 
         
        //  第二种方式:
        Outter.Inner inner1 = outter.getInnerInstance();
    }
}
 
class Outter {
    private Inner inner = null;
    public Outter() {
         
    }
     
    public Inner getInnerInstance() {
        if(inner == null)
            inner = new Inner();
        return inner;
    }
      
    class Inner {
        public Inner() {
             
        }
    }
}

内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。

局部内部类:

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

class People{
    public People() {
         
    }
}
 
class Man{
    public Man(){
         
    }
     
    public People getWoman(){
        //  局部内部类
        class Woman extends People{   
            int age =0;
        }
        return new Woman();
    }
}

因为是定义在方法中的类,方法调用执行后就会被释放掉。
注意:局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。

匿名内部类:

匿名内部类可以使你的代码更加简洁,你可以在定义一个类的同时对其进行实例化。它与局部类很相似,不同的是它没有类名,如果某个局部类你只需要用一次,那么你就可以使用匿名内部类

HelloWorld frenchGreeting = new HelloWorld() {
public class HelloWorldAnonymousClasses {

    /**
     * 包含两个方法的HelloWorld接口
     */
    interface HelloWorld {
        public void greet();
        public void greetSomeone(String someone);
    }

    public void sayHello() {

        // 1、局部类EnglishGreeting实现了HelloWorld接口
        class EnglishGreeting implements HelloWorld {
            String name = "world";
            public void greet() {
                greetSomeone("world");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hello " + name);
            }
        }

        HelloWorld englishGreeting = new EnglishGreeting();

        // 2、匿名类实现HelloWorld接口
        HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };

        // 3、匿名类实现HelloWorld接口
        HelloWorld spanishGreeting = new HelloWorld() {
            String name = "mundo";
            public void greet() {
                greetSomeone("mundo");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hola, " + name);
            }
        };
        
        englishGreeting.greet();
        frenchGreeting.greetSomeone("Fred");
        spanishGreeting.greet();
    }

    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }
}
}

上面是官方文档中给的例子:
运行结果:

Hello world
Salut Fred
Hola, mundo

该例中用局部类来初始化变量englishGreeting,用匿类来初始化变量frenchGreeting和spanishGreeting,两种实现之间有明显的区别:

1)局部类EnglishGreetin实现HelloWorld接口,有自己的类名,定义完成之后需要再用new关键字实例化才可以使用;
2)frenchGreeting、spanishGreeting在定义的时候就实例化了,定义完了就可以直接使用;
3)匿名类是一个表达式,因此在定义的最后用分号";"结束。

匿名内部类的语法:

/*
*  实现接口的匿名类
*/
HelloWorld frenchGreeting = new HelloWorld() {
  String name = "你好";
  public void greet() {
        greetSomeone("你好啊");
  }
  public void greetSomeone(String someone) {
       name = someone;
       System.out.println("你好 " + name);
  }
};

/*
*  匿名子类(继承父类)
*/
public class AnimalTest {
 
     private final String ANIMAL_NAME = "动物";
 
     public void accessTest() {
         System.out.println("匿名内部类访问其外部类方法");
     }
 
     class Animal {
         private String name;
 
         public Animal(String name) {
             this.name = name;
         }
 
         public void printAnimalName() {
             System.out.println(bird.name);
         }
     }
 
     // 鸟类,匿名子类,继承自Animal类,可以覆写父类方法
     Animal bird = new Animal("麻雀") {
 
         @Override
         public void printAnimalName() {
             // 访问外部类成员
             accessTest(); 
             // 访问外部类final修饰的变量                  
             System.out.println(ANIMAL_NAME);  
             super.printAnimalName();
         }
     };
 
     public void print() {
         bird.printAnimalName();
     }
 
     public static void main(String[] args) {
 
         AnimalTest animalTest = new AnimalTest();
         animalTest.print();
     }
 }
匿名内部类访问其外部类方法
动物
麻雀

从以上两个实例中可知,匿名类表达式包含以下内部分:
1、操作符:new;
2、一个要实现的接口或要继承的类,案例一中的匿名类实现了HellowWorld接口,案例二中的匿名内部类继承了Animal父类;
3、一对括号,如果是匿名子类,与实例化普通类的语法类似,如果有构造参数,要带上构造参数;如果是实现一个接口,只需要一对空括号即可;
4、一段被"{}"括起来类声明主体;
5、末尾的";"号(因为匿名类的声明是一个表达式,是语句的一部分,因此要以分号结尾)。

访问作用域内的局部变量、定义和访问匿名内部类成员

匿名内部类与局部类对作用域内的变量拥有相同的的访问权限。
(1)、匿名内部类可以访问外部内的所有成员;
(2)、匿名内部类不能访问外部类未加final修饰的变量(注意:JDK1.8即使没有用final修饰也可以访问);
(3)、属性屏蔽,与内嵌类相同,匿名内部类定义的类型(如变量)会屏蔽其作用域范围内的其他同名类型(变量):

内嵌类的属性屏蔽:

public class ShadowTest {

    public int x = 0;

    class FirstLevel {

        public int x = 1;

        void methodInFirstLevel(int x) {
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
        }
    }

    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}
x = 23
this.x = 1
ShadowTest.this.x = 0

这个实例中有三个变量x:1、ShadowTest类的成员变量;2、内部类FirstLevel的成员变量;3、内部类方法methodInFirstLevel的参数。
methodInFirstLevel的参数x屏蔽了内部类FirstLevel的成员变量,因此,在该方法内部使用x时实际上是使用的是参数x,可以使用this关键字来指定引用是成员变量x:

System.out.println("this.x = " + this.x); 

利用类名来引用其成员变量拥有最高的优先级,不会被其他同名变量屏蔽,如:

System.out.println("ShadowTest.this.x = " + ShadowTest.this.x); 

匿名内部类的属性屏蔽:

public class ShadowTest {
    public int x = 0;

    interface FirstLevel {
     void methodInFirstLevel(int x);
    }

    FirstLevel firstLevel =  new FirstLevel() {

        public int x = 1;

        @Override
        public void methodInFirstLevel(int x) {
            // 接口传入的参数x
            System.out.println("x = " + x);
            // new FirstLevel()中定义的 int x = 1;
            System.out.println("this.x = " + this.x);
            // 调用外部类的成员变量 int x = 0;
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
        }
    };

    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.firstLevel;
        fl.methodInFirstLevel(23);
    }
}
x = 23
this.x = 1
ShadowTest.this.x = 0

注意:
1、匿名内部类中不能定义静态属性、方法;
2、匿名内部类可以有常量属性(final修饰的属性);
3、匿名内部内中可以定义属性
4、匿名内部内中可以可以有额外的方法(父接口、类中没有的方法);
5、匿名内部内中可以定义内部类;
6、匿名内部内中可以对其他类进行实例化。

静态内部类:

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

public class Test {
    public static void main(String[] args)  {
        Outter.Inner inner = new Outter.Inner();
    }
}
 
class Outter {
    public Outter() {
         
    }
     
    static class Inner {
        public Inner() {
             
        }
    }
}

Java 枚举(enum)

Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等。
Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割。

enum Color 
{ 
    RED, GREEN, BLUE; 
} 
enum Color
{
    RED, GREEN, BLUE;
}
 
public class Test
{
    // 执行输出结果
    public static void main(String[] args)
    {
        Color c1 = Color.RED;
        System.out.println(c1);
    }
}

values(), ordinal() 和 valueOf() 方法

enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Seriablizable 和 java.lang.Comparable 两个接口。
values(), ordinal() 和 valueOf() 方法位于 java.lang.Enum 类中:
values() 返回枚举类中所有的值。
ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。
valueOf()方法返回指定字符串值的枚举常量。

enum Color
{
    RED, GREEN, BLUE;
}
 
public class Test
{
    public static void main(String[] args)
    {
        // 调用 values()
        Color[] arr = Color.values();
 
        // 迭代枚举
        for (Color col : arr)
        {
            // 查看索引
            System.out.println(col + " at index " + col.ordinal());
        }
 
        // 使用 valueOf() 返回枚举常量,不存在的会报错 IllegalArgumentException
        System.out.println(Color.valueOf("RED"));
        // System.out.println(Color.valueOf("WHITE"));
    }
}
RED at index 0
GREEN at index 1
BLUE at index 2
RED

枚举类成员

enum Color
{
    RED, GREEN, BLUE;
 
    // 构造函数
    private Color()
    {
        System.out.println("Constructor called for : " + this.toString());
    }
 
    public void colorInfo()
    {
        System.out.println("Universal Color");
    }
}
 
public class Test
{    
    // 输出
    public static void main(String[] args)
    {
        Color c1 = Color.RED;
        System.out.println(c1);
        c1.colorInfo();
    }
}
Constructor called for : RED
Constructor called for : GREEN
Constructor called for : BLUE
RED
Universal Color

今天复习到这里,明天开始复习Java集合、之后是IO,线程、异常处理、JDBC再之后就开始复习框架了,从SSM开始吧!sturts2、hibernate就不复习了,之后简单看下就行了,配置太多了。之后学习下Springboot、Springcloud。做个简单的小项目。

你可能感兴趣的:(2021-12-06、12-07)