内部类
- 将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
- 为什么要声明内部类呢? 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,不在其他地方单独使用,那么整个内部的完整结构最好使用内部类。而且内部类因为在外部类的里面,因此可以直接访问外部类的私有成员。
- 根据内部类声明的位置(如同变量的分类),我们可以分为:成员内部类(静态成员内部类丶非静态成员内部类)丶局部内部类(有名字的局部内部类丶匿名的内部类)。在实际开发中,我们很少使用内部类,一般情况内部类都出现在JDK源码中。下面我们来了解一下常用的内部类..
成员内部类
和其他类一样,它只是定义在外部类中的另一个完整的类结构
- 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
- 可以在非静态内部类中声明属性、方法、构造器等结构,但是不允许声明静态成员,但是可以继承父类的静态成员,而且可以声明静态常量。
- 可以使用abstract修饰,因此它也可以被其他类继承
- 可以使用final修饰,表示不能被继承
- 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。
和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private
- 外部类只允许public或缺省的
- 还可以在非静态内部类中使用外部类的所有成员,哪怕是私有的
- 在外部类的静态成员中不可以使用非静态内部类哦,就如同静态方法中不能访问本类的非静态成员变量和非静态方法一样
- 外部类要访问内部类的成员,必须要建立内部类的对象。 因此在非静态内部类的方法中有两个this对象,一个是外部类的this对象,一个是内部类的this对象
创建内部类对象格式:
代码示例
// 外部类
class Body {
public void methodW1(){
// 访问内部类的成员
//Body.Heart bh = new Body().new Heart();
Heart bh = new Heart();
System.out.println(bh.numN);// 10
bh.methodN1();// 内部类的成员方法 methodN1
}
// 成员变量
private int numW = 100;
// 成员方法
private void methodW2(){
System.out.println("外部类的成员方法 methodW2");
}
// 内部类
public class Heart{
// 成员变量
int numN = 10;
// 成员方法
public void methodN1(){
System.out.println("内部类的成员方法 methodN1");
}
public void methodN2(){
// 访问外部类的成员
System.out.println(numW);
methodW2();
}
}
}
public class Test {
public static void main(String[] args) {
/*
- 成员内部类的访问特点:
在其他类中,访问内部类的成员,得先创建内部类对象:
外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
在外部类中,访问内部类的成员,得先创建内部类对象:
内部类名 对象名 = new 内部类名();
在内部类中,可以直接访问外部类的一切成员(包含私有的):
*/
// 创建内部类的对象
Body.Heart bh = new Body().new Heart();
System.out.println(bh.numN);// 10
bh.methodN1();// 内部类的成员方法 methodN1
System.out.println("=======================");
// 创建外部类对象
Body b = new Body();
b.methodW1();
System.out.println("=======================");
bh.methodN2();// 100 外部类的成员方法 methodW2
}
}
匿名内部类
匿名内部类是局部内部类的简化写法。它的本质是一个带具体实现的
父类或者父接口的
匿名的
子类对象。
格式:
注意:
- 匿名内部类是一种特殊的局部内部类,只不过没有名称而已。所有局部内部类的限制都适用于匿名内部类。
- 作用:调用某个方法丶赋值给父类/父接口的变量,通过多态引用使用这个对象丶作为某个方法调用的实参
代码示例
interface A{
public abstract void show();
}
class Imp implements A{
public void show(){
System.out.println("实现类实现show方法");
}
}
public class Test {
public static void main(String[] args) {
/*
匿名内部类:
概述:本质是一个接口的匿名实现类的对象
格式:
new 接口名(){
实现抽象方法
};
*/
// 需求:调用A接口的show方法
// 1.创建实现类实现A接口
// 2.在实现类中重写show方法
// 3.创建实现类对象
// 4.使用实现类对象调用show方法
Imp imp = new Imp();// imp就是接口的实现类的对象
imp.show();
System.out.println("==============================");
// 简化: 匿名内部类
A a = new A() {//通过父类或父接口的变量多态引用匿名内部类的对象
@Override
public void show() {
System.out.println("匿名内部类");
}
};
a.show();//调用方法
}
}
引用类型使用小结
实际的开发中,引用类型的使用非常重要,也是非常普遍的。我们可以在理解基本类型的使用方式基础上,进一步去掌握引用类型的使用方式。基本类型可以作为成员变量、作为方法的参数、作为方法的返回值,那么当然引用类型也是可以的。下面我们就进行小结一下:
- 类作为成员变量:类作为成员变量时,对它进行赋值的操作,实际上,是赋给它该类的一个对象或者子类对象。
代码示例:
class IdCard {
String idNum;// 身份证号码
String address;// 地址
public IdCard(String idNum, String address) {
this.idNum = idNum;
this.address = address;
}
public IdCard() {
}
}
class Person {
String name;// 引用数据类型定义成员变量 String类
int age;// 基本类型定义成员变量
IdCard idCard;//类作为成员变量
public Person(String name, int age, IdCard idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
public Person() {
}
public void show() {
System.out.println("我的名字\t:" + name + " 年龄:\t" + age);
System.out.println("身份证号码\t:" + idCard.idNum + " 地址:\t" + idCard.address);
}
}
public class Test {
public static void main(String[] args) {
IdCard idCard = new IdCard("12334", "火星");
Person person = new Person("张三", 18, idCard);
person.show();
}
}
- 类作为参数时:表示可以接收此类的对象或者子类对象作为实参;
- 类作为返回值类型时:表示可以返回此类的对象或者子类对象
- 抽象类作为形参:表示可以接收任何此抽象类的"子类对象"作为实参;
- 抽象类作为返回值:表示"此方法可以返回此抽象类的任何子类对象";
- 接口作为方法的形参:表示可以接收任何此接口的实现类的"子类对象"作为实参;
- 接口作为方法的返回值:表示"此方法可以返回此接口的实现类的任何子类对象";
- 抽象类作为成员变量:为此成员变量赋值时,可以是任何它的子类对象
- 接口作为成员变量:为此成员变量赋值时,可以是任何它的实现类对象