Java内部类与接口回调
1.内部类
内部类顾名思义就是定义在一个类中的类。内部类可以分为:普通内部类、静态内部类、局部内部类、匿名内部类。
2.普通内部类
1.定义方式
将内部类定义在一个类的方法外,且非static修饰的就是普通内部类。内部类限定词可以为public
、private
、protected
。
当为private
时,只有在外部类的方法中可以构造它。
当为protected
时,需要写限定词为public的内部类构造函数才能在子类中调用构造函数。
class Outer{
private String name = "outer";
public class Inner{
private String name = "inner";
}
}
2.创建内部类对象的方式
- 在外部类方法中可以使用如下创建方式:
Inner inner = this.new Inner();
- 对于
public
内部类,也可以先创建一个外部类对象再创建内部类对象:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
3.访问权限
- 内部类可以访问外部类的成员,包括
private
成员。 - 外部类可以调用内部类成员,但是需要通过内部类对象来调用它的成员,这样间接的方式,因为它自身是不具备这些成员的。
- 内部类对于同一个包下的其他类来说,是无法被看到的。
package test;
public class innerClass{
public static void main(String[] args) {
Outer outer = new Outer();
test.Outer.Inner inner = outer.new Inner();
//outer.sayName(); outer自身没有这个方法,所以通过outer对象是无法直接调用内部类inner的成员方法的
outer.sayNameByInner(inner); // 但是这样通过内部类inner对象调用就是可以的。
System.out.println("===========");
inner.sayName();
}
}
class Outer{
private String name = "outer";
public String getInnerName(Inner inner){
return inner.name; //可以通过inner对象来间接调用其私有成员。
// 可能这样的形式不够明显,但是如果将outer和inner改为两个类会更明显一些。
// 在分开写成两个类的情况下,outer是无法访问inner的name属性的,就好比在main方法中写如下代码:outer.name,是错的。
// 这证明写成内部类的形式可以让外部类访问内部类的私有成员。
}
public void sayNameByInner(Inner inner){
inner.sayName();
}
public class Inner{
private String name = "inner";
public void sayName(){
sayInnerName();
sayOuterName();
}
private void sayInnerName(){
System.out.println(name);
}
private void sayOuterName(){
System.out.println(Outer.this.name); //通过类名.this.属性名 来取得外部类的属性
}
}
}
结果:
inner
outer
===========
inner
outer
就好像房东与租客的关系。房东有一套房子,租给了租客住,房东的洗衣机冰箱等设施,虽然属于房东,但是租客是可以随意用的。
突然有一天房东需要用锤子,刚好租客自己买了一个,那么房东就可以问租客借他的锤子,在征求得他的意见之后就可以拿到了。
3.静态内部类
1.定义方式
在普通内部类中,内部类构造器中会被编译器自动加上对外部类的引用,这样才能保证可以访问外部类的所有成员。
有时候, 使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象。为此,可以将内部类声明为 static, 以便取消产生的引用。静态内部类限定词可以为public
、private
、protected
。
class Outer2{
private String name = "outer";
private static int age = 18;
static class inner2{
private String name = "inner";
private static int age = 18;
}
2.静态内部类对象的创建
静态内部类对象的创建可以脱离外部类实体,可以先不创建外部类就创建内部类,而非静态内部类就必须先实例化外部类对
象。
Inner2 inner2 = new Outer2.Inner2();
此外,静态类的静态方法、静态属性可以通过类名 + 成员名访问。
System.out.println(Inner2.getOuterAge()); // 假设在Inner类中有一个getOuterAge的静态方法
3.访问权限
- 内部类只能访问外部类静态成员
- 外部类可以访问内部类所有成员(间接方式)。
class Outer2{
private String name = "outer";
private static int age = 18;
public static int getAge(){ // 内部类可以访问外部类的静态方法
return age;
}
public String getInnerName(Inner2 inner2){
return inner2.name; // 外部类可以访问内部类非静态属性
}
public int getInnerAge(Inner2 inner2){
return inner2.age;// 外部类可以访问内部类静态属性
}
static class Inner2{
private String name = "inner";
private static int age = 18;
public int getOuterAge(){
//return Outer2.age; 这样也可以访问,内部类可以访问外部类的静态属性
return getAge();
}
//public String getOuterName(){
// return Outer2.name; //静态内部类只能访问外部类的静态成员
//}
}
public static void main(String[] args) {
Inner2 inner2 = new Outer2.Inner2();
Outer2 outer2 = new Outer2();
System.out.println(outer2.getInnerName(inner2));
System.out.println(outer2.getInnerAge(inner2));
System.out.println("==========");
System.out.println(inner2.getOuterAge());
}
}
结果:
inner
18
==========
18
4.总结
1.静态内部类的特点
(1)使用static修饰的成员内部类叫静态内部类
(2)静态内部类跟外部类没有任何关系,只是在生成类名和类定义时有影响。静态内部类可以看做是与外部类平级的类,使用方式与外部类平级的类完全相同。
(3)创建静态内部类的实例,使用 外部类名.内部类名 实例名 = new 外部类名.内部类构造方法;
2.静态内部类有以下限制
(1)静态内部类不能与外部类重名
(2)静态内部类不能访问外部类的非静态的属性和方法,外部类也不能访问内部类的非静态的属性和方法。
4.局部内部类
1.定义方式
如果我们需要的内部类只在一个方法中被用到,就可以只在这个方法内定义一个内部类,这就是局部内部类,类似局部变量的定义。
class Outer3{
public void test(){
class Inner3{
....
}
}
}
局部类不能用 public 或 private 访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。
2.优点
局部类有一个优势, 即对外部世界可以完全地隐藏起来。 即使 Outer 类中的其他方法也不能访问它。除 test 方法之外, 没有任何方法知道Inner类的存在。
3.访问局部变量
与其他内部类相比较,局部类还有一个优点。它们不仅能够访问包含它们的外部类, 还可以访问局部变量。不过,那些局部变量必须事实上为 final
。这说明, 它们一旦赋值就绝不会改变。
class Outer3{
public void test(String name,int age){ //可以将参数声明为final保证不会被修改
class Inner3{
public void say(){
System.out.println(name);
System.out.println(age);
//age++; 不能修改局部变量
}
}
Inner3 inner3 = new Inner3();
inner3.say();
}
public static void main(String[] args) {
Outer3 outer3 = new Outer3();
outer3.test("李华",18);
}
}
5.匿名内部类
1.定义方式
定义类的最终目的是创建一个类的实例,但是如果某个类的实例只是用一次,则可以将类的定义与类的创建,放到与一起完成,或者说
在定义类的同时就创建一个类,以这种方法定义的没有名字的类成为匿名内部类。匿名内部类像是进一步减少使用范围的局部内部类。
class Outer4{
public void say(){
Inner inner = new Inner() {
....
}
};
}
}
可能会疑惑这里明明是有类名Inner的,为什么说匿名内部类是创建一个没有类名的对象呢?那是因为这里的Inner是指一个接口或者超类,而我们实际定义的类是{}中的部分。
2.一个实例
匿名内部类必须继承一个类或实现一个接口,这里的Inner是匿名内部类所实现的接口名。但匿名内部类不能同时实现一个接口和继承一个类,也不能实现多个接口。
如果实现了一个接口,该类是Object类的直接子类,匿名类继承一个类或实现一个接口,不需要extends和implements关键字。
interface Inner{
void sayInner();
}
class Outer4{
public void say(){
Inner inner = new Inner() {
@Override
public void sayInner() {
System.out.println("hi");
}
};
inner.sayInner();
}
public static void main(String[] args) {
Outer4 outer4 = new Outer4();
outer4.say();
}
}
由于构造器的名字必须与类名相同, 而匿名类没有类名,所以,匿名类不能有构造器。取而代之的是,将构造器参数传递给超类构造器。尤其是在内部类实现接口的时候, 不能有任何构造参数。