目录
【内部类是什么?】
内部类 —— 就是把一个类放在类里面定义
内部类 - 寄生类
外部类 - 宿主类
内部类,由于踏上一级程序单元是类,因此他完全可以使用 private | protected | 默认 | public 这四个访问控制符
由于内部的上一级程序单元是类,可以用 static 修饰。有 static 修饰的类属于外部类本身;没有 static 修饰的类属于外部类的实例。
【为什么要引入内部类?】
- 为了更好地封装,可以把内部类隐藏在外部类中,不允许同一个包内其他类来访该类(在该类内有效,在其他类无用处)
- 内部类成员可以直接访问外部类的私有数据
- 匿名内部类适合用于创建那些仅需要使用一次的类
【注意】非静态外部类不能拥有静态内部类。(为什么?)【注释4】
【如何使用内部类?】
非静态内部类
内部类有一个好处:他可以直接访问外部类的 private 成员(field/方法/构造器);反过来,外部类不能访问内部类成员
- 若是没有 static 修饰的内部类的实例,必须寄生在“外部类”的对象里。
- 若有 static 修饰的内部类的实例,寄生在“外部类”的类本身里
以下代码表明。调用寄生实例方法之前,必须创建宿主实例
/**
* @ClassName: Cow
* @description:
* @author:FFIDEAL
* @Date: 2020年2月27日22:30:24
*/
public class Cow {
private double weight;
public Cow() {
}
public Cow(double weight) {
this.weight = weight;
}
// 定义一个非静态内部类
private class CowLeg {
// 定义两个非静态类实例
private double length;
private String color;
public CowLeg() {
}
public CowLeg(double length, String color) {
this.length = length;
this.color = color;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
// 创建非静态内部类的实例方法
public void info() {
System.out.println("当前牛腿的颜色是" + color + ",牛腿的长度为" + length);
// 可以调用当前外部类的private修饰的成员变量
System.out.println("当前牛的体重是:" + weight);
}
}
// 在非静态内部类中,要创建内部类的实例,首先要创建宿主实例
public void test() {
CowLeg c1 = new CowLeg(1.12, "黄色");
c1.info();
}
public static void main(String[] args) {
Cow cow = new Cow(112.6);
cow.test();
}
}
非静态内部类,也属于非静态成员 —— 因为不能被外部静态成员所访问
【若查找某个变量时】
当在非静态内部类的方法访问某个变量时:在该方法内查找 -> 在方法所在类中查找 -> 在该类的外部类查找 ->系统出现编译错误
/**
* @ClassName: DiscernVariable
* @description: 描述内部类调用外部类成员变量、内部类成员变量和局部成员变量时的语句
* @author: FFIDEAL
* @Date: 2020年2月27日 下午10:37:29
*/
/**
* @author dell
*
*/
public class DiscernVariable {
private String prop = "我在早上学习Java!";
// 创建内部类
private class InClass {
private String prop = "我在中午学习Java!";
// 创建内部类的一个测试方法
public void info() {
String prop = "我在晚上学习Java!";
// 通过外部类名.this.varName访问外部类实例变量
System.out.println("外部类的实例变量值为" + DiscernVariable.this.prop);
// 通过this.varName访问外部类实例变量
System.out.println("内部类的实例变量值为" + this.prop);
// 直接访问局部变量
System.out.println("局部变量值为" + prop);
}
}
// 创建一个方法用于创建内部类的实例
public void test() {
InClass ic = new InClass();
ic.info();
}
// 创建一个宿主实例,用宿主实例调用上述方法
public static void main(String[] args) {
DiscernVariable dv = new DiscernVariable();
dv.test();
}
}
【注意】在非静态内部类中,所有的方法、成员变量都属于实例的。所以在创建内部实例之前,若没有外部类,则无法获得外部宿主实例。反之,在static修饰的内部类中,在运行时,已经加载了外部类,存在了宿主类,就可以创建内部静态类的对象,该对象寄生在外部类中
静态内部类
静态内部类有 static 修饰,寄生在“外部类”的类本身里,而不属于任何外部类的实例。静态内部类可以包含静态成员,也可以包含非静态成员。但是静态内部类的实例方法,也是属于静态成员,因此他不能访问外部类的非静态成员。
- 以上为什么不能用类名调用?【注释5】
/**
* @ClassName: StaticInnerClassTest
* @description:静态内部类的实例方法也不能访问外部类的实例方法,只能访问外部类的静态成员
* @author: FFIDEAL
* @Date: 2020年2月27日 下午11:02:48
*/
public class StaticInnerClassTest {
private String str1 = "这是非静态字符串";
private static String str2 = "这是静态字符串";
static class StaticInnerClass {
// 静态内部类可以包含静态成员
private static int age;
public void access() {
// System.out.println(str1);代码错误。静态内部类不能访问外部类的非静态成员变量
System.out.println(str2);
}
}
public static void main(String[] args) {
new StaticInnerClass().access();
}
}
使用内部类
- 定义变量
- 创建实例
- 访问它的类 field/类方法
【在外部类以内使用内部类】
若在外部类的里面,使用内部类。并没有太多需要注意的地方。只需要指导不要在外部类的静态成员中使用非静态外部类,因为静态成员不能访问非静态成员。
【在外部类以外使用非静态内部类】
- 声明变量:外部类.内部类 变量名
- 创建非静态内部类的实例:在此之前,必须先创建外部类的实例 —— 必须先创建宿主。接下来用“宿主.new 外部类构造器()”
- 非静态内部类派生子类:由于子类的构造器,必须调用父类的构造器一次,因此必须在子类构造器中使用宿主对象去调用非静态内部类的构造器
/**
* @ClassName: CreateInnerInstance
* @description: 在外部类以外使用非静态内部类
* @author: FFIDEAL
* @Date: 2020年2月29日 下午1:00:46
*/
class Out{
//创建一个非静态内部类
class In{
public In(String msg) {
System.out.println("调用一个内部类构造器,传进的参数是:"+msg);
}
}
}
public class CreateInnerInstance {
public static void main(String[] args) {
Out.In in = new Out().new In("在学习Java的时候不要忘记学习Java虚拟机");
/*Out类中的in类,先new一个Out类的对象,在new一个In的对象,这样就有宿主了
* 相当于
* 使用OutClass.InnerClass的形式调用内部类变量
* Out.In in;
* 创建外部类宿主对象
* Out out = new Out();
* 通过外部类实例和new来调用内部类构造器创建非静态内部类实例
* in = out.new In("在学习Java的时候不要忘记学习Java虚拟机");
*/
}
}
【在外部类以外使用非静态内部类】
- 静态内部类它是寄生外部类的类本身里面的,所以一般不需要程序员去理会宿主,可以把外部类当做静态内部类的包就行了。只需要外部类就可以调用构造器了。
/**
* @ClassName: CreateStaticInnerClassTest
* @description: 测试在外部类以外使用静态内部类
* @author: FFIDEAL
* @Date: 2020年2月29日 下午1:17:32
*/
class StaticOut{
//创建一个静态内部类
static class StaticInner{
public StaticInner() {
System.out.println("静态内部类构造器");
}
}
}
public class CreateStaticInnerClassTest {
public static void main(String[] args) {
new StaticOut.StaticInner();
}
}
局部内部类
局部内部类是一个局部成员,和局部变量相似,因此不能用 static 、访问控制符修饰。局部内部类用 final 修饰是可以的,辨明该局部内部类不能被继承。局部内部类的作用域实在是太有限了 —— 除了方法就不能使用了。由于局部内部类只能在当前方法中使用,因此使用起来比较简单。局部内部类的class名为:外部类$N个内部类.class(为什么要有N)【见注释1】
匿名内部类
它是没有名字的内部类,所以程序以后无法再访问这个“匿名内部类”。当程序创建匿名内部类时,会立即创建匿名内部类的实例。
//匿名内部类的语法
new 接口() | 父类构造器(参数){
//类主体
}
// —— 上述语法格式实际上返回匿名内部类的实例。程序以后只能使用匿名内部类的实例
/**
* @ClassName: AnonymousTest
* @description: 实现接口来创建内部类:new 实现接口
* @author: FFIDEAL
* @Date: 2020年2月29日 下午2:33:10
*/
interface Products{
public double getPrice();
public String getName();
}
public class AnonymousTest {
//需要传入一个Product实例
public void test(Products p) {
System.out.println("购买一个"+p.getName()+",话费了"+p.getPrice());
}
public static void main(String[] args) {
AnonymousTest at = new AnonymousTest();
at.test(new Products() {
public double getPrice() {
return 5560;
}
public String getName(){
return "华为手机";
}
});
}
}
/**
* @ClassName: AnonymousInner
* @description: 继承父类创建内部类:new 实现接口,匿名内部类将拥有和父类相似的构造器
* @author: FFIDEAL
* @Date: 2020年2月29日 下午6:30:06
*/
abstract class Device{
private String name;
public abstract double getPrice();
public Device() {}
public Device(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class AnonymousInner {
public void test(Device d) {
System.out.println("又买了一个"+d.getName()+",花掉了"+d.getPrice());
}
public static void main(String[] args) {
AnonymousInner ai = new AnonymousInner();
//调用有参数的构造器创建Device的匿名类实例对象
ai.test(new Device("示波器"){
public double getPrice() {
return 55.6;
}
});
//调用无参数的构造器
Device d = new Device() {
//初始化块
{
System.out.println("匿名内部类的初始化块");
}
//实现抽象方法
public double getPrice() {
return 56.2;
}
//重写父类的方法
public String getName() {
return "键盘";
}
};
ai.test(d);
}
}
【匿名内部类的规则】
- 匿名内部类必须显式继承一个父类,或实现【一个】接口
- 匿名内部类必须实现接口或抽象类中所有抽象方法(为什么?)【见注释2】
- 匿名内部类不能有构造器 (为什么?)【见注释3】
【注释1】因为一个外部类,不同方法中可以定义多个同名的局部内部类
【注释2】因为匿名内部类需要立即创建市里,因此他不能是抽象的
【注释3】因为匿名内部类没有名字,构造器名字不能确定
【注释4】首先静态成员不能访问非静态成员。若静态内部类位于非静态外部类之内,那么静态内部类不能访问外部类,且那就失去了静态内部类存在的意义。
【注释5】因为类中的static是属于类本身的。但是static修饰类就与里面非static修饰的方法无关。该方法依然是非静态方法。静态内部类属于外部类而已。非静态内部类属于内部类实例。但创建这个内部类实例之前,要确保有外部类的实例这个宿主存在