默认的访问修饰符
最初的发现是遇到了一个疑问,Java中类、接口、内部类、抽象类中属性方法的默认访问修饰符是什么?
稍微查了查资料
- 普通类默认的访问修饰符是:default,也即包内友好
- 接口中的属性的默认是public static final ,方法是public abstract
- 内部类默认的访问修饰符是:public,只是要依附与外围类
- 抽象类默认的访问修饰符是:default
注意
Java中外围类、接口、抽象类的访问修饰符只能是public和default
成员内部类访问修饰权限的测试
这里还可以看一看如何实例化成员内部类,(滑稽
前期准备
- 包
InPackage
中有一个OutClass
,里面有一个内部类InClass
作为成员变量 - 同一个包下有一个测试类
InPackage_OutClass
- 不在同一个包下有一个测试类
OutPackage_OutClass
成员内部类的实例化方法
OutClass.InClass in = new OutClass.InClass(); //只有在内部类中的外围类中可以使用
// 1.通用
OutClass.InClass in2 = new OutClass().new InClass();
// 2.通用
OutClass out = new OutClass();
OutClass.InClass in3 = out.new InClass();
实验
测试的方法就是看能否实例化成员内部类对象
- 首先是直接在外围类中测试
/**
* public > protected > default > private
* private 不能修饰外部类
* protected 不能修饰外部类
*/
public class OutClass {
// 作为成员内部类,内部类拥有其外围类的所有成员的访问权
// 不加修饰符的情况下,其修饰符是 default
public class InClass{
//public static int id; // static变量违法
// 内部类不能有static方法,因为内部类必须依赖与外部类存在
public void print(){
System.out.println("this inner class");
}
}
@Test
public void testInClass(){
// 1.成员内部类的一种初始化方法
InClass in = new InClass();
in.print();
// 2.成员内部类的另一种初始化方法
OutClass.InClass in2 = new OutClass().new InClass();
in2.print();
// 3.使用外部类绑定来初始化
OutClass out = new OutClass();
OutClass.InClass in3 = out.new InClass();
// 4.当内部类是static修饰时可以使用这个初始化方式
//InClass in4 = new OutClass.InClass();
}
}
- 接着是“同包不同类”中实验
/**
* 该类旨在测试同一个包下,是否可以访问内部类
*
* 所以访问权限是,只有依附与外部类,内部类是可以访问的;但无法直接访问,即便是public也不行
*/
public class InPackage_OutClass {
@Test
public void testInClass(){
OutClass out = new OutClass();
// 1.同一个包的情况下,可以访问内部类
OutClass.InClass in = new OutClass().new InClass();
// 不使用.new便无法初始化对象
// OutClass.InClass in2 = new OutClass.InClass(); // 在内部类的外围类中可以使用该初始化方法
// 2.使用外部类对象绑定初始化内部类
OutClass.InClass in3 = out.new InClass();
// 在同一个包的情况下,可以直接导入,但原理还是与上面一致
InClass in4 = out.new InClass();
}
}
- 接着是“不同包不同类”
/**
* 本例旨在测试不在同一个包的情况下,默认修饰的内部类的访问权限
*
* 所以访问权限是,只有依附与外部类,内部类是可以访问的;但无法直接访问
*/
public class OutPackage_OutClass {
public void testInClass(){
OutClass out = new OutClass();
// 1.该方法继承或不继承都可以使用
OutClass.InClass in = new OutClass().new InClass();
// 2.外围类对象绑定的方法可以初始化
OutClass.InClass in2 = out.new InClass();
// 该方法与上述原理一样
InClass in3 = out.new InClass(); // 不行
}
}
结论
从上面的实验可以看出,成员内部类的默认访问修饰符是public,这个public其实是指在依赖于外部类的情况下来看这个内部类其实是public的(其实一开始认为是default,直到发现没有继承时也可通过外围类进行实例化…….)
有疑问的地方
其实上述的结论是不敢确定的,因为还看到了这个:Java内部类与访问修饰符
所以,真相究竟是什么? (ㄒoㄒ)
成员内部类的用途
其实一旦观察到了这个知识点,总是很容易会想到用途这个实际的东西(…….),看了看《Think In Java》,里面有这么一个例子:
interface Context{
public void print();
}
/**
* 内部类的主要用途是在类向上转型(接口)时隐藏实现细节
*/
public class InnerClassApplication {
class InClass implements Context{
@Override
public void print(){
System.out.println("我是实现接口的成员内部类");
}
}
public Context getContext(){
return new InClass();
}
@Test
public void testInClassApplication(){
InnerClassApplication icap = new InnerClassApplication();
icap.getContext().print();
}
}
解释一下就是,使用内部类实现一个接口或者继承一个基类,然后向上转型可以隐藏实现的细节
再来看看其它的内部类
局部内部类
首先来看为什么要这样用,无非是有这样的需求:
- 实现了某类型的接口,然后隐藏实现返回接口的实例化对象
- 希望有一个非公共的类来完成某些特殊的任务
普通局部内部类
定义在一个方法中,作用域就是该方法,相较于成员内部类,就是直接把类放入了方法体中
匿名内部类
这个就有说头了,匿名就是类没有名字,由JVM自动分配一个特殊助记名
public class Test{
public Context getContextFromNoNameClass(){
return new Context(){
@Override
public void print(){
System.out.println("我是隐式实现了接口的匿名内部类");
}
};
}
@Test
public void testNoNameInClass(){
// 可以看到的是尽管没有显式的实现Context接口,但还是重写了print()方法
this.getContextFromNoNameClass().print();
System.out.println(this.getContextFromNoNameClass().getClass());
}
}
这个内部类的类名是class InnerClass.InnerClassApplication$1
,重点是$1
,这个就是JVM给匿名内部类分配的助记符
匿名内部类的一些注意
匿名类不可以改变外围类中的属性,所以当在匿名内部类中使用外部类对象时,会要求这个外围类对象加上final
修饰符
嵌套类
说白了就是给成员内部类加上了static
的修饰符,这意味着
- 要创建嵌套类的对象并不需要依附与外围类(长能耐了)
- 不能从嵌套类的对象中访问到非静态的外围类对象
- 同时可以使用InClass in = new OutClass.InClass();
这种实例化方法
为什么要有内部类
神说要有光,于是便有了光。那为什么会需要内部类呢?
“每个内部类都能独立的继承自一个(接口的)是实现,所以无论外围类是否已经继承了(接口的)实现,对内部类都没有影响” ——《Think in Java》
除此之外,私以为还有上述提到的隐藏实现细节这个原因,所以掌握熟练也是很有必要的,工业界应该少不了这个要求吧?!