内部类,不是在一个java源文件中编写俩个平行的类,而是在一个类的内部再定义另外的一个类。
内部类有四种:成员内部类;静态内部类;局部内部类;匿名内部类。
1.成员内部类:
(1)成员内部类中,不能编写静态的属性和方法。
(2)代码编译后,会产生两个class文件,一个对应外部类,一个对应内部类:
MemberOuterClass.class;
MemberOuterClass$MemberInnerClass.class;
(3)成员内部类和外部类的相互访问:
1)成员内部类访问外部的属性和方法,直接通过类名访问;包括私有的。(此时的内部类可以被看做外部类的一个成员。)
2)外部类访问成员内部类的属性和方法,要先创建内部类对象,然后才可以访问,通过对象名.属性访问。包括私有的。
3)如果这个成员内部类不是private修饰的,那么在其他类中就可以访问到这个内部类。使用的方法:
1>这个内部类需要import导入,并且是外部类.内部类的形式导入。
2>在创建对象的时候,需要先创建出外部类对象,然后使用外部类对象再创建内部类对象。形式为: 外部类对象.new 内部类对象();。
(4)成员内部类的私有使用:private修饰成员内部类,可以将内部类理解成一个私有成员。可以在外部类其他方法中使用成员私有内部类。
(5)静态成员内部类:把静态成员内部类看成是一个私有成员。
成员内部类被静态修饰后的访问方式是:
外部类.内部类 对象名 = new 外部类.内部类(…);
static内部类里面所有方法都可以看成 static的,只能访问外部类中static成员[属性、方法]。
2.静态内部类:类似于成员内部类,但多了static修饰。
(1)静态内部类中,可以编写静态的属性和方法,另外在四种内部类中,只有静态内部类可以编写静态属性和方法。
(2)编译生成的俩个class文件的名字分别为:
StaticOuterClass.class;
StaticOuterClass$StaticInnerClass.class;
(3)静态内部类和外部类的相互访问:
1)静态内部类中,无法访问外部类的非静态属性和方法;可以访问外部类的静态属性和方法;
2)外部类中,访问静态内部类中静态属性:直接通过静态内部类名.属性;访问静态内部类中的非静态属性和方法:先定义内部类对象。
3)如果这个静态内部类不是private修饰的,那么在其他类中就可以访问到这个内部类。使用方法:
1>这个内部类需要import导入,并且是 外部类.内部类 的形式导入。
2>在创建对象的时候,直接使用这个静态内部类的名字:new 静态内部类对象();,不再需要依赖外部类对象。
Integer类中私有静态内部类IntegerCache
的作用是什么?
缓存byte范围的int类型数(-128~127)。
==
表示要比较这个俩个对象的内存地址是否相等。
3.局部内部类:
(1)局部内部类的位置:局部内部类,是另一种形式的内部,在声明在外部类的方法中,相当于方法中的局部变量的位置,它的作用范围只是在当前方法中。局部内部类是最不常用的一种内部类。
(2)局部内部类和外部类的相互访问:
1)局部内部类访问外部的属性和方法的形式:外部类名.属性或者外部类名.方法名();
2) 局部内部类中,访问当前方法中的变量,这个变量必须是final修饰的。
3)外部类访问局部内部类的属性和方法的形式:创建局部内部类对象,通过创建的对象访问属性和方法。
(3)局部内部类在访问其所在方法中的局部变量必须用final修饰,为什么? 因为当调用这个方法时,局部变量如果没有用final修饰,那么其生命周期和方法的生命周期是一样的,当方法(弹栈)调用完,栈空间中的这个局部变量也会消失,那么如果局部内部类对象还没有马上消失,并且这个对象想用这个局部变量,但这时局部变量已经不存在,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也可以继续使用。(这种方式也被称为延迟生命周期)
(4)在JDK1.8中,一个局部变量在局部内部类中进行访问了,那么这个局部变量自动变为final修饰。
4.匿名内部类:
匿名内部类,是一种没有名字的内部类,它是内部类的一种简化写法。匿名内部类是使用最多的一种内部类。
使用匿名内部类的前提是:已经存在一个类或者接口,这个类可以是具体类也可以是抽象类。
(1)在普通的代码中,使用一个接口的步骤如下:
1)声明一个类,去实现这个接口;
2)实现这个接口中的抽象方法(重写)
3)在其他代码中,创建这个类的对象
4)调用类中实现(重写)后的方法
目的就是把接口中的抽象方法给实现(重写)了,最后再调用到这个实现后(重写)的方法。
(2)使用匿名内部类,就可以把这个过程给给简化,让我们更加方便的调用到实现(重写)后的方法!匿名内部类的格式:
父类或者接口类型 变量名 = new 父类或者接口(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
//获取一个匿名对象
//调用实现(重写)后的方法:变量名.method();
(3)匿名内部类因为没有类名,
1)匿名内部类必须依托于一个父类型或者一个接口:
1>利用父类型来声明并创建匿名内部类对象:如果利用父类型声明这个匿名内部类,那么这个匿名内部类默认就是这个父类型的子类。
public interface Action {
void run();
}
class Test{
public static void main(String[] args) {
Action a = new Action(){
@Override
public void run() {
System.out.println("匿名内部类中的默认实现");
}
};
a.run();
}
}
2>利用接口来声明并创建匿名内部类对象:
//Algorithm 算法接口
public interface Algorithm{
void sort(int[] arr);
}
class Test{
//使用指定算法,对数组arr进行排序
public void sort(int[] arr,Algorithm alg){
alg.sort(arr);
}
}
public static void main(String[] args){
Test t = new Test();
int[] arr = {4,1,6,3,8,5,9};
Algorithm alg = new Algorithm(){
public void sort(int[] arr){
//使用当前需要的排序算法
//例如,这里简单的使用Arrays工具类中的排序方法
java.util.Arrays.sort(arr);
}
};
t.sort(arr,alg);
}
2)匿名内部类在声明的同时,就必须创建出对象,否则后面就没法创建了
3)匿名内部类中无法定义构造器。
(4)匿名内部类重写多个方法调用?
匿名内部类只可以一次调用一种方法。若有多个方法,那么都要用匿名内部类去调用,需要建造多次匿名内部类。
(5)匿名内部类的应用?
package com.briup.day04;
abstract class Person {
public abstract void show();
}
class PersonDemo {
public void method(Person p) {
p.show();
}
}
class PersonTest {
public static void main(String[] args) {
//如何调用PersonDemo中的method方法呢?
PersonDemo pd = new PersonDemo();
//先让父类引用指向子类对象
Person p = new Student();
//然后直接调用PersonDemo类中的method方法
pd.method(p);
}
}
在确定使用内部类的情况下,选择哪一种内部类呢?
1)考虑这个内部类,如果需要反复的进行多次使用(必须有名字)
1>在这个内部类中,如果需要定义静态的属性和方法,选择使用静态内部类;
2>在这个内部类中,如果需要访问外部类的非静态属性和方法,选择使用成员内部类。
2)考虑这个内部类,如果只需要使用一次(可以没有名字):选择使用匿名内部类。
3)局部内部类,几乎不会使用。
类的内部除了嵌套另一个类之外,是否还可以嵌套接口?
可以,不仅类中可以嵌套接口,接口的内部也可以嵌套其他接口。接口中嵌套接口:(1)java.util.Map接口中的内部接口Entry;(2)Stream里面嵌套一个Builder;(3)Executor中的ExecutorService。
什么情况下会使用内部类?
在对事物进行抽象的时候,若一个事物内部还包含其他事物,就可以考虑使用内部类这种结构。比如车(Car)中有引擎(Engine)。
正常情况下,编写一个类,都可以使用哪些权限控制修饰符?
正常编写的类,可以使用俩种权限控制修饰符:public和default;
但是,如果是内部类的话,则可以使用四种权限控制修饰符。