内部类
为什么要使用内部类?在《Think in java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
其实使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性
1、什么是内部类
- 在类中定义的类,称之为内部类
- 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立
- 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类
- 内部类是一个独立的实体,可以把内部类当成X来看待,类里面多了一个X
- 内部类提供了更好的封装,除了该外围类,其他类都不能访问,私有内部类
特点
- 编译之后课生成独立的字节码文件
- 内部类可以访问外部类的私有成员,且不破坏封装
- 可为外部类提供必要的内部功能组件
2、 内部类之成员内部类
- 定义:在类中的类,属于类的成员,创建成员内部类对象必须依赖外部类对象
- 成员内部类中不能存在任何static的变量和方法
- 成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类
- 成员内部类可以继承类,也可以实现接口,即实现多继承
- 成员内部类的实例
OutClass oc = new OutClass();
OuterClass.InnerClass oi = oc.new InnerClass();
具体使用
package com.qianfeng.xqc.day0228.cy;
import com.qianfeng.xqc.day0228.cy.OuterClass.InnerClass;
import com.qianfeng.xqc.day0228.cy.OuterClass.InnerClassB;
public class Test {
public static void main(String[] args) {
OuterClass oc = new OuterClass();//实例化一个外部类的对象
System.out.println("通过外部类对象oc调用外部类中实例化的内部类对象ic调用内部类的inShow()方法:");
oc.ic.inShow();
System.out.print("OuterClass继承");
oc.a();
InnerClass ic1 = oc.new InnerClass();//实例化内部类的对象,内部类的实例化必须要依赖外部类的一个对象
System.out.println("访问了外部类的私有属性num");
ic1.testPrivate();
InnerClassB ic2 = oc.new InnerClassB();
System.out.print("OuterClass的内部类InnerClassB继承");
ic2.b();
}
}
class OuterClass extends A{//外部类,继承A
String name = "外部类";
private int num = 10;
public void outShow() {
System.out.println("out String name is "+name);
}
class InnerClass{//成员内部类
String name = "内部类";
public void inShow() {
String name = "tom"; // 成员内部类的局部变量
System.out.println("in String name is " + name);
System.out.println("内部类的局部变量 : " + name);// 打印是什么内容
System.out.println("内部类的成员变量 : " + this.name);// 打印是什么内容
// OuterClass.this 外部类的当前对象
System.out.println("外部类的成员变量 : " + OuterClass.this.name);// 基本上不会用到
}
public void testPrivate() {//直接访问了外部类的私有属性
System.out.println("访问外部类的私有属性num="+num);
}
}
//外部类的本身可以直接实例化成员内部类
InnerClass ic = new InnerClass(); // 有一个默认的无参构造器
class InnerClassB extends B{//通过内部类可以实现多继承
}
class InnerClassC extends C {// 通过内部类可以实现多继承
}
}
class A{
public void a() {
System.out.println("A独有的方法");
}
}
class B{
public void b() {
System.out.println("B独有的方法");
}
}
class C{
public void c() {
System.out.println("C独有的方法");
}
}
3、内部类之静态内部类
- 定义:在类中的类,class关键字前加入 static 修饰符
- 创建静态内部类对象不需要外部类对象
- 静态内部类不能使用任何外围类的非static成员变量和方法
- 静态内部类的实例
InnerClass ic = new InnerClass();//创建一个静态内部类的对象,一般不用
OuterClass.InnerClass.in1();//直接通过类名.类名来访问我们静态内部类里面的静态元素
具体用法
package com.qianfeng.xqc.day0228.sta;
import com.qianfeng.xqc.day0228.sta.OuterClass.InnerClass;
public class Test {
public static void main(String[] args) {
InnerClass ic = new InnerClass();//创建一个静态内部类的对象
ic.a();//没有被static修饰过的,必须通过对象来访问
OuterClass.outShow();//外部类的静态方法直接访问
//直接通过类名.类名来访问我们静态内部类里面的静态元素
OuterClass.InnerClass.in1();
System.out.println(OuterClass.InnerClass.name);
}
}
class OuterClass{//外部类
static String name = "外部类";
int age = 10;
public static void outShow() {//静态方法
System.out.println("外部类的静态方法的静态属性name:"+name);
}
static class InnerClass{//静态内部类
static String name = "静态内部类";
static int num = 20;
static void in1() {
System.out.println("静态内部类的静态方法:"+OuterClass.name);
// System.out.println(age);//静态方法里面不能访问非静态属性
}
void a() {
// System.out.println(age);//静态类里面不能访问非静态属性
System.out.println("静态内部类的非静态方法");
}
}
}
4、内部类之局部内部类
有这样一种内部类,它是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效
-
定义:在方法中的类,作用范围只在当前方法中
-
如果当前变量只是被引用,没有具体使用;则默认不会添加final
-
如果当前变量被引用,并且被使用;jdk1.8后会自动为变量添加上final关键字
-
在方法中实例化局部内部类,在外面使用外部类对象调用该局部方法
package com.qianfeng.xqc.day0228.jb;
public class Test {
public static void main(String[] args) {
OuterClass oc = new OuterClass();
oc.test(); //调用这个test,咱们这个局部内部类才会存在和使用;
System.out.println("方法完成,我这个实例会存在");
}
}
class OuterClass{
public void test() {//成员方法
int num = 20;//局部变量//加上final,他的生命周期并不是随着方法结束;而是没有任何一个地方引用到这个变量,才会死亡
/**
* 局部内部类;
* 使用范围 : 方法内部
* 生命周期; 实例被GC(垃圾回收器)回收的时间点,而不是方法完成的时间点
*/
class JuBuInnerClass{
public void fun1() {
System.out.println("局部内部类的方法" + num);
// num += 10;//局部变量会被final修饰
}
}
//我们真正使用这个num的时候,编译器会默认添加一个final的关键字来修饰这个局部变量
JuBuInnerClass jc = new JuBuInnerClass();
jc.fun1();
}
}
应用: 有些情况,不方便对外暴露我们整个类以及使用类做什么事情的情况下
学校老师的分配
package com.qianfeng.xqc.day0228.jb;
public class TestSchool2 {
public static void main(String[] args) {
School ch = new School();
//按要求应该是调用School的getTeacher方法选老师
//传入一个班级编号,根据编号来返回不同的讲师,不主动选择了
Teacher tc = ch.getTeacher(201);
tc.teach();
//但是成员内部类,有些人就直接自己new一个super讲师
// BigTeacher bt = ch.new BigTeacher();//当使用局部内部类的时候就不能跳过学校,自己选择老师
// bt.teach();
}
}
//老师讲课接口
interface Teacher{
public void teach();//讲课
}
//school安排teacher讲课
class School{
public Teacher getTeacher(int num) {
// 定义局部内部类,实现Teacher接口,安排高级讲师上课
class BigTeacher implements Teacher {
public void teach() {
System.out.println("高级讲师");
}
}
//安排超级讲师上课
class SuperTeacher implements Teacher {
public void teach() {
System.out.println("超级讲师");
}
}
if(num % 3 == 0) {//三个班中 有一个分配的是超级讲师
return new SuperTeacher();
}else {
return new BigTeacher();
}
}
}
5、 内部类之匿名内部类
- 特殊的局部内部类,没有类名,只能作为子类出现且只能创建一个对象
- 必须要实现一个接口,或者继承一个类,但是两者不可兼得,同时也只能继承一个类或者实现一个接口,而且必须要实现继承的类或者实现的接口的所有抽象方法
- 必须要借助于父类或者接口去进行实例化
- 匿名内部类中不能定义构造函数,不能存在任何的静态成员变量和静态方法
具体用法
package com.qianfeng.xqc.day0228.nm;
public class Test {
public static void main(String[] args) {
OuterClass oc = new OuterClass();
oc.test();
}
}
interface Apple{
public void eat();
}
class ABC{
public void sleep() {
System.out.println("abc sleep");
}
}
class OuterClass{
public void test() {
class InnerClass{}//局部内部类
//匿名内部类-实现接口
Apple ap = new Apple() {//大括号就是我们的匿名内部类,所以我们要在这个类里面,重写这个接口的eat方法
public void eat() {//重写Apple接口的eat方法
System.out.println("匿名内部类eat apple");
}
};
ap.eat();//调用匿名内部类的eat方法
Object obj = new Object();//实例化一个Object类
Object obnm = new Object() {};//实例化一个Object子类(匿名内部类)
//匿名内部类-实现继承
ABC abc = new ABC() {
public void sleep() {//重写ABC的sleep方法
super.sleep();//调用父类sleep方法
System.out.println("匿名内部类 smoke");
}
};
abc.sleep();//调用
}
}
学校老师的分配(优化-使用匿名内部类)
package com.qianfeng.xqc.day0228.nm;
public class TestSchoolEnd {
public static void main(String[] args) {
School ch = new School();
Teacher tc = ch.getTeacher(201);
tc.teach();
}
}
//老师讲课接口
interface Teacher{
public void teach();//讲课
}
//school安排teacher讲课
class School{
public Teacher getTeacher(int num) {
if(num % 3 == 0) {//三个班中 有一个分配的是超级讲师
return new Teacher() {//返回匿名内部类
public void teach() {
System.out.println("超级讲师");
}
};
}else {
return new Teacher() {
public void teach() {
System.out.println("高级讲师");
}
};
}
}
}