外部类
我们在创建类的时候,创建的 .java
文件(源文件)的文件名必须和我们要创建的类的名字一样。而这个我们创建的类,就是外部类, 也叫顶级类。
做一点引申:
- 在一个源文件里面,可以有很多个顶级类, 但是这些顶级类只有和文件名一样的顶级类才能被 public 修饰, 只有一个,其他的都只能用默认(什么都不加)进行修饰。
- 同一个源文件里面的多个顶级类,因为只有一个可以被 public 修饰,所以导致其他的顶级类,只能在当前包路径下使用。
- 《Effective Java, Third Edition》第25条建议: 每个源文件只对应一个顶级类
内部类
又称之为嵌套类,是在类中在定义另外一个类,这个类可以在顶级类里面的任意地方
1. 静态内部类
public class OutClass {
public static class InnerClass {
}
}
直接在顶级类的内部声明, 通过 static 进行修饰的类
特点:
- 所有的权限修饰符都可以进行修饰
- 能够在类能定义静态属性和方法
- 只能使用外部类的静态属性和方法
- 在其他类里面调用静态内部类(静态内部类与外部类没有关系, 在编译后,就是2个普通的类)
OutClass.StaticInnerClass innerClass = new OutClass.StaticInnerClass();
2. 非静态内部类
1. 成员内部类
public class OutClass {
public class InnerClass {
}
}
在顶级类的直接内部,和类的属性同级
特点:
- 所有的权限修饰符都可以进行修饰
- 不能够在类内定义静态属性和方法
- 能够访问外部类的所有属性和方法, 包括静态的
- 在其他类里面调用成员内部类(成员内部类对象会隐式的引用一个外部类对象, 也就是2者之间有联系)
OutClass outClass = new OutClass();
// 需要先创建出外部类的实例,通过实例才能创建内部类
OutClass.InnerClass innerClass = outClass.new InnerClass();
2. 局部内部类
public class OutClass {
public void fn() {
class InnerClass {
}
}
}
声明在顶级类的某个作用域内(方法体内, if 判断里面等)
特点:
- 这个类只能使用默认权限修饰符, 不能被其他的权限修饰符修饰
- 不能够在类内定义静态属性和方法
- 可以访问到外部类的属性和方法,包括静态的
- 在其他类是无法调用到局部内部类的
- 局部类只能在对应的作用域起作用, 超出了对应的作用域就没了,所以无法被其他的类调用
3. 匿名内部类
public class OutClass {
public void fn() {
/**
* 创建格式:
* new 父类/接口(参数列表) {
* }
*/
new Thread(new Runnable() {
@Override
public void run() {
}
});
}
}
在顶级类的某个作用域内, 直接创建使用, 没有进行具体的声明
特点:
- 匿名内部类为局部内部类的特例, 具备局部内部类的特点
内部类如何访问外部类的同名方法/属性
内部类的属性, 方法和外部类的重名了,在调用时,优先使用内部类的, 同时可以通过外部类.this.方法/属性
,访问到外部类的同名方法/属性
局部内部类访问方法体内入参/变量的条件
- 局部内部类可以访问方法内的局部变量和入参,但是前提是这个变量和入参在这个方法体内是被 final 修饰过的。
- 在 jdk8 中, 可以直接声明后使用,只要你保证你的入参/变量满足
Effectively final
注: Effectively final
是 jdk8 新增的一个特性。(对于一个变量,如果没有给它加final修饰,而且没有对它的二次赋值,那么这个变量就是effectively final(有效的不会变的)。简单的来说,我们在方法 fn 里面声明了一个 变量 int a = 1; 在这个方法体内,除了第一次声明外,没有别的地方对其进行了修改, 他就是 effectively final
public class OutClass {
public void fn() {
int a = 123;
class InnerClass {
public void innerFn() {
// 内部类 调用 外部的变量, 在 jdk8 之前, a 必须用 final 继续修饰
// 但是在 jdk8 下, 我们的 a 除了声明式做了赋值, 在整个方法体内没进行其他的修改了
// 此时 我们的 a 是 effectively final 的, 可以在内部类进行使用
System.out.println("a : " + a);
}
}
// 如果把下面的 a 赋值 注释去掉, 会报错, a 不是 effectively final 了
// a = 1233;
}
}
作用
- 封装性, 隐藏具体的细节, 不让外部类知道
/**
* 加密器接口
*/
public interface Encrypter {
/**
* 对内容进行加密
* @param content 加密的内容
* @return 加密后的内容
*/
String encrypt(String content);
}
/**
* 外部类
*/
public class OutClass {
/**
* 隐藏起来的加密器
*/
private class MyEncrypter implements Encrypter {
@Override
public String encrypt(String content) {
// 在需要加密的内容后面加上 encrypt
return content + "encrypt";
}
}
/**
* 获取加密器
* @return
*/
public Encrypter getEncrypter() {
return new MyEncrypter();
}
}
/**
* 调用类
*/
public class Main {
public static void main(String[] args) {
String str = "需要加密的内容";
// 取到加密器, 此处我们只知道通过外部类就能获得加密器,但是加密器的加密过程我们是不知道的
OutClass outClass = new OutClass();
Encrypter encrypter = outClass.getEncrypter();
// 加密内容
String encryptedContent = encrypter.encrypt(str);
System.out.println(encryptedContent);
}
}
- 间接实现多重继承(声明多个内部类,每个内部类继承一个类)
/**
* 加密器
*/
public class Encrypter {
/**
* 对内容进行加密
* @param content 加密的内容
* @return 加密后的内容
*/
public String encrypt(String content) {
// 在需要加密的内容后面加上 encrypt
return content + "encrypt";
}
}
/**
* 解密器
*/
public class Decrypter {
/**
* 对内容进行解密
* @param content 需要解密的内容
* @return 解密后的内容
*/
public String decrypt(String content) {
// 在需要加密的内容后面加上 encrypt
int length = "encrypt".length();
// 解密的内容不为空,同时长度需要大于 length
if (content == null || "".equals(content) || content.length() < length) {
return content;
}
return content.substring(0, content.length() - length );
}
}
/**
* 外部类,此时我们的外部类具备了编码和解码的功能
*/
public class OutClass {
private class MyEncrypter extends Encrypter{
}
private class MyDecrypter extends Decrypter{
}
public String encrypt(String content) {
return new MyEncrypter().encrypt(content);
}
public String decrypt(String content) {
return new MyDecrypter().decrypt(content);
}
}
- 方便编写事件驱动程序(如安卓里面的按钮事件等)
/** 匿名内部类 */
myBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//这里写代码
}
});
网友评论