内部类观其名知其意。内部类就是将类定义再另一个类的内部,该类就称为内部类。
但是内部类在声明所在的位置不同有具体分四种:成员内部类、局部内部类、匿名内部类和静态内部类
内部类的特点:
- 内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员,但是下面会具体说所谓的所有而非绝对,例如静态内部类只能调用静态的成员或方法)。
成员内部类
成员内部类:位于外部类的成员位置的类
特点:内部类可以访问外部类的成员变量和成员方法,那怕是外部类的privatei修饰的方法和属性。
具体格式如下:
public class Out {
private int outInt=10;
class Inner{
public int innerInt=20;
private void show() {
System.out.println("成员内部类");
System.out.println("内部成员类调用外部outInt==="+outInt);
test();//可以直接调用外部的方法 不会报错
}
}
public void test() {
System.out.println("Out的方法");
}
public static void main(String[] args) {
// System.out.println(innerInt);//i cannot be resolved to a variable 这个时候会有一个报错,就是无法识别这个变量
Out.Inter Inner=new Out().new Inner();
inter.show();
}
}
//输出
成员内部类
内部成员类调用外部outInt===10
Out的方法
通过上面可以得知其特征可以得知了内部类可以直接调用外部类的一切,但是外部类却需要实例化内部类才可以调用内部类的方法。
这样看虽然Inter虽然是一个类,但是其又像是Out的成员。所以Out称之为外部类,而Inter为内部类。
对内部类实例化格式
out类.inner类 对象名=new out类.new inner类();
成员内部类常用的修饰符修饰:
- private:一般不写默认就是private修饰,毕竟既然是成员内部类一般都是外部类使用的,当然如果有其他类要调用的话,就需要在外部类写一个方法,通过外部类进行调用。
- static:如果成员内部类由static修饰的话, 那就是静态内部类了,具体在讲静态内部类的时候再具体聊。
public class Out {
private int outInt=10;
private class Inner{
public int innerrInt=20;
private void show() {
System.out.println("成员内部类");
System.out.println("内部成员类调用外部outInt==="+outInt);
test();//可以直接调用外部的方法 不会报错
}
}
public void test() {
System.out.println("Out的方法");
}
public Inner getInnerr() {
//这个地方可以写new Out().new Inter();
return new Inner();//其实这个是 this.new Inter()
}
public static void main(String[] args) {
// System.out.println(innerInt);//i cannot be resolved to a variable 这个时候会有一个报错,就是无法识别这个变量
Out.Inner Inner=new Out().new Inner();
inter.show();
}
}
补充:
看以下代码,理解以下java变量的特点:
public class Out {
public String flag = "Out 属性 flag";
class Inner {
public String flag = "Inner 属性 flag";
public void show() {
String flag = "Inner show中变量 flag";
System.out.println(flag);// Inner show中变量 flag
System.out.println(this.flag);// Inner 属性 flag
System.out.println(Out.this.flag);// Out 属性 flag
}
}
public static void main(String[] args) {
Out.Inner inner=new Out().new Inner();
inner.show();
}
}
局部内部类
局部内部类其实局部内部变量差不多,在方法中或者作用域的声明的内部类即为局部内部类。
public class Out {
public void showOut() {
class Inner{
}
}
}
注意:
- 局内内部类就像是方法里面的局部变量一样,是不能由Public,private,protected以及static修饰符修饰。
- 局部内部类的作用域名只能再自身所在的方法和属性中使用,也就是无法在其所在的方法或者作用域外new出。
- 当然也是无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)
具体使用如下:
public class Out {
private String outFlag="out--flag";
public void showOut() {
final String outMethonFlag="out-methon-flag";
// outMethonFlag="test";
class Inner{
String innnerFlag="inner--flag";
// innnerFlag="";
public void showInner() {
System.out.println(outFlag);
//如果局部内部类调用,其所在方法或者作用域声明的变量,那么此变量必须是final修饰,而其虽然不写也就默认其为final。
System.out.println(outMethonFlag);
System.out.println(innnerFlag);
System.out.println("局部内部类的方法");
}
}
Inner inner=new Inner();
inner.showInner();
}
public static void main(String[] args) {
Out out =new Out();
out.showOut();
}
}
//输出
out--flag
out-methon-flag
inner--flag
局部内部类的方法
补充:
如果局部内部类调用,其所在方法或者作用域声明的变量,那么此变量必须是final修饰,而其虽然不写也就默认其为final。
为什么如此,因为局部联络是随着方法的调用而调用,也就是使用完后就会消失,但是java的垃圾回收机制决定了其使用后的数据不会马上再堆中消失。也就是变量名没有了,但是在内部类中调用的话,自然希望其名可以消失,但是其值还需要保留的,这个时候就需要用final进行修饰的,也就是当用final修饰的变量,其堆内存直接会存储这个值,而不是存储指向这个值的变量名。
静态内部类
在创建类的时候,本身是无法使用static进行修饰,但是其内部类却可以,因为可以将其看作一个外部类的成员。所以可以用static修饰了,而这种被static修饰的内部类就为静态内部类的。
具体格式如下:
class Out{
static class Inner{
}
}
既然用static修饰了,也就有了很多静态属性的特征:
- 在创建的时候可以直接用外部类进行创建内部类对象。
- 静态内部类自然也是可以调用外部类的属性和方法,但是必须是static修饰的,否则无法调用。
package test;
public class Out {
static String flag="out--flag";
static class Inner{
public void show() {
System.out.println("内部方法"+flag);
}
public static void showStatic() {
System.out.println("内部static方法 "+flag);
}
}
public static void main(String[] args) {
// 静态方法或属性可以通过类或者new的对象进行调用
System.out.println(Out.flag);
System.out.println(new Out().flag);
// 但是静态内部类却只能通过类进行创建,而无法用new出的外部对象在创建内部类
// Inner in=new Out.new Inner(); 会报错
Inner in=new Out.Inner();
// 内部对象可以调用静态和非静态方法,与常见对象这个方面没有区别
in.show();
in.showStatic();
// 当然也可以通过内部类直接调用内部的静态方法,不需要创建内部类对象
Out.Inner.showStatic();
}
}
//输出
out--flag
out--flag
内部方法out--flag
内部方法out--flag
内部static方法 out--flag
调用静态内部类格式
静态内部类 内部类名=new 外部类.静态内部类()
// 对比看一下成员内部类的区别
内部类 内部类名=new 外部类().内部类()
匿名内部类
明面上的意思:一个没有名字的类,是内部类的简化写法。
匿名内部类时平常编写程序的时候使用频率很高的一种内部类。
其格式如下:
new 类名或者接口名(){
重写方法();
}
现在看例子:
public class Out {
private String flag="out private ";
public void showOut() {
new InnerImp() {
@Override
public void show() {
System.out.println("我是一个匿名内部类");
System.out.println(flag);
}
}.show();
}
public static void main(String[] args) {
new Out().showOut();
}
}
interface InnerImp{
void show();
}
//输出
我是一个匿名内部类
out private
而匿名内部类一般都是在编程的时候,一般调用某个接口实现类方法一次的时候,才会通过匿名内部类来实现。
而且匿名内部类在调用所在方法或者作用域中的变量时候,和局部内部类一样的限制。
补充:因为1.8讲匿名内部类的中的接口内只有一个方法的时候,可以通过lambda格式进行实现,这个可以看一下另一篇。java基础 浅解1.8新增lambda表达式
内部类的使用意义
封闭性
这是内部类最重要的一点,因为方便控制某个类只能让某个类调用,也就是内部类被外部类调用,而在调用的时候,我们可以通过private修饰内部类,让其他类无法所以访问,而只能通过外部类的public修饰的方法间接调用,也就是访问允许其访问的内部类的数据。
如下(这个在成员内部类中也展示过)
public class Out {
private int outInt=10;
private class Inner{
public int innerrInt=20;
private void show() {
System.out.println("成员内部类");
System.out.println("内部成员类调用外部outInt==="+outInt);
test();//可以直接调用外部的方法 不会报错
}
}
public void test() {
System.out.println("Out的方法");
}
public Inner getInnerr() {
//这个地方可以写new Out().new Inter();
return new Inner();//其实这个是 this.new Inter()
}
public static void main(String[] args) {
// System.out.println(innerInt);//i cannot be resolved to a variable 这个时候会有一个报错,就是无法识别这个变量
Out.Inner Inner=new Out().new Inner();
inter.show();
}
}
继承更加多样化
每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。同时内部类实现某个接口或继承某个类可以方便被外部类间接调用。这样外部类看着不会那样累赘。
package test;
public class Out {
class Inner implements InnerImp{
@Override
public void show() {
System.out.println("我实现了接口,然后运行了show方法");
}
}
class Inner1 implements InnerImp1{
@Override
public void play() {
System.out.println("我实现了接口,然后运行了play方法");
}
}
public static void main(String[] args) {
Out out=new Out();
Out.Inner inner=out.new Inner();
inner.show();
Out.Inner1 inner1=out.new Inner1();
inner1.play();
}
}
interface InnerImp{
void show();
}
interface InnerImp1{
void play();
}
//输出
我实现了接口,然后运行了show方法
我实现了接口,然后运行了play方法
通过上面可以看出,需要实现某些接口的时候,而外部类需要实现的接口太多了,而有些接口方法可以通过内部类实现,而让代码看起来没有那样的复杂。
(ps 对于内部类应该前面聊一下的,但是忘了,在要写浅谈map接口的时候用到了内部类,所以写了此篇。)