学Java,内部类,累不累?(冷笑话)
目录
一、内部类的理解
什么是内部类?我们对普通的类已经很理解了,比如人类(person)、动物类(animal)、书类(book),突然发现这些类似乎都比较广泛,或者说更贴近一个大的整体。如果一个整体的内部结构很复杂,比方说人类(person),你光说这个人叫什么名(name),今年几岁(age),是男是女(gender)……似乎都只是很概括性地描述人这个类,如果要把人类更细致的结构描述出来,作为人类你有五官、有五脏六腑、有骨骼、肌肉,不难发现,前面举例的这些“零件”似乎也能单独视作一个类,(比如)眼睛类->单双眼皮、眸色……
因此,为了更细致地描述一个大的类,可以用内部类去描述这个大类中可以单独“拆分”出来的“零件”,这样既贴切我们的认知,又方便代码的维护
二、四种内部类
分别是:成员内部类、局部内部类、静态内部类、匿名内部类
①成员内部类
1)定义方式
在一个类中,我们已经熟知的,可以定义:成员变量、成员方法;
如果在Outer类中定义一个Inner类,则称Inner是Outer的一个内部类,相对的,可以称Outer是Inner的外部类
代码示例:
class Person {
String name;
int age;
class Eye {
String eyelid;//单双眼皮
String Color;//眸色
}
}
在这个示例中,Peron类拥有两个成员属性和一个成员内部类,name和age分别用来描述姓名和年龄,eyelid和Color分别用来描述单双眼皮和眸色,当然你完全可以把这两个属性直接作为Person类的成员属性,但如果采用内部类的形式书写->既贴切我们的认知,又方便代码的维护
2)成员内部类访问外部成员
成员内部类作为外部类的一个成员,自然是可以访问外部类的属性的,代码示例:
class Person {
String name = "张三";
int age = 18;
class Eye {
String eyelid;//单双眼皮
String Color;//眸色
void print() {//定义一个print函数,作为Eye这个内部类的成员方法
System.out.println(name + "今年" + age + "岁");
//访问外部类的name和age两个属性
}
}
}
3)成员内部类实例化对象
用成员内部类实例化对象的公式如下:
外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
解释:
等号左边,用外部类名.的形式访问到内部类名;
等号右边,理解为->先用new 外部类名()实例化了一个外部类对象,然后用这个外部类对象.的形式访问到内部类名,此时再用这个内部类名去new一个内部类对象
代码示例;
public class Main {
public static void main(String[] args) {
//外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
Person.Eye e1 = new Person().new Eye();
e1.print();
}
}
class Person {
String name = "张三";
int age = 18;
class Eye {
String eyelid;//单双眼皮
String Color;//眸色
void print() {//定义一个print函数,作为Eye这个内部类的成员方法
System.out.println(name + "今年" + age + "岁");
}
}
}
②局部内部类
局部内部类的定义和使用——
局部内部类,也叫作方法内部类;
在一个类的成员方法中定义一个内部类,这个类就属于局部内部类,因为它的作用范围(生命周期)只局限在这个类中;
在局部内部类中,局部内部类可以访问外部类的所有成员变量和方法,而局部内部类中变量和方法却只能在所属方法中访问;
public class Main {
public static void main(String[] args) {
#用Person实例化对象并调用它的成员方法func
Person p = new Person();
p.func();
}
}
class Person {
String name = "张三";
int age = 18;
void func() {
class Eye {
String eyelid;//单双眼皮
String Color;//眸色
void print() {//定义一个print函数,作为Eye这个内部类的成员方法
System.out.println(name + "今年" + age + "岁");
}
}
//局部内部类,只能在func里访问,因此在此处实例化内部类Eye对象e并调用print方法
Eye e = new Eye();
e.print();
}
}
上述代码中,定义了一个Person类,在Person类中定义了一个成员方法func,在func方法中定义了一个内部类Eye,故称Eye是Person类的局部内部类,其中——
Eye类可以访问外部类Person的所有成员,但Eye这个局部内部类的属性和方法只能在func这个方法里面访问!
③静态内部类
1)静态内部类的特点
在形式上,就只是在成员内部类的基础上加以static关键字修饰
在功能上,
㈠静态内部类只能访问外部类的静态成员;
㈡访问静态内部类的成员时,可以跳过它的外部类
2)静态内部类实例化对象
用静态内部类实例化对象的公式如下:
外部类名.内部类名 变量名 = new 外部类名.内部类名();
解释:
等号左边和成员内部类是一样的,不再赘述
等号右边,我们知道类的静态成员是可以通过类名直接访问的,因此直接用外部类名.的形式可以访问到静态内部类,然后用这个静态内部类去new一个静态内部类对象
示例用来理解以上两点:
public class Main {
public static void main(String[] args) {
//外部类名.内部类名 变量名 = new 外部类名.内部类名();
Person.Eye e = new Person.Eye();
e.print();
}
}
class Person {
static String name = "张三";
int age = 18;
static class Eye {
String eyelid;//单双眼皮
String Color;//眸色
void print() {
System.out.println("姓名是" + name);//name是外部类的静态成员,可以正常访问
//System.out.println("年龄是" + age);错误语法!静态内部类只能访问外部类的静态成员
}
}
}
④匿名内部类(重难)
1)理解
所谓匿名,就是没有名字,匿名内部类,就是没有名字的内部类;
我们经常会遇到一个对象只用一次的情况,这种情况下如果用匿名内部类,就可以不必创建新的子类去继承(extends)或实现(implements),然后才能用子类去实例化对象
我个人的理解:直接把匿名类对象看作是一个具体的变量(已经被实例化好的具体对象)
2)匿名内部类的形式
匿名内部类有两种定义形式,㈠继承父类;㈡实现接口
代码示例:
interface Mouth {
String test2 = "这是嘴类,我把它作为一个接口";
void Mouth_print();
}
abstract class Eye {
String test1 = "这是眼睛类,我把它作为一个父类";
abstract void Eye_show();
}
public class Main {
public static void main(String[] args) {
new Eye() {//继承父类的匿名内部类
@Override
void Eye_show() {
System.out.println("调用Eye父类的test成员属性:" + test1);
}
};
new Mouth() {
@Override
public void Mouth_print() {
System.out.println("调用Mouth接口的test成员属性" + test2);
}
};
}
}
补充内容(进阶操作,可不看):
㈠找到字节码文件(.class)
㈡在当前目录打开命令行窗口(cmd)
㈢对两个字节码文件分别进行反编译
㈣得到反编译结果
分析:显然Eye是我们定义的父类,而Mouth是定义的接口,因此内部类1隐藏的是extends关键字,内部类2隐藏的是implements关键字
3)匿名内部类的使用
对于一个匿名内部类,可以直接把它视作成一个实例化的对象,因此可以完成以下操作——
㈠直接访问成员
㈡作为参数,传参给其它方法
㈣测试的完整代码:
interface Mouth {
String test2 = "这是嘴类,我把它作为一个接口";
void Mouth_print();
}
abstract class Eye {
String test1 = "这是眼睛类,我把它作为一个父类";
abstract void Eye_show();
}
public class Main {
public static void main(String[] args) {
test_func(
new Eye() {//继承父类的匿名内部类
@Override
void Eye_show() {
System.out.println("调用Eye父类的test成员属性:" + test1);
}
},//这里要注意,既然把这个匿名内部类看作一个具体对象,那么函数传多个参数的时候,显然应该用逗号隔开
new Mouth() {
@Override
public void Mouth_print() {
System.out.println("调用Mouth接口的test成员属性" + test2);
}
}
);
}
static void test_func(Eye e, Mouth m) {//参数列表和传参一定要注意一一对应
e.Eye_show();
m.Mouth_print();
}
}