一、概述

  1、引入

    类的成员包括:

1、属性:成员变量
2、方法:成员方法
3、构造器
4、代码块
5、内部类:成员内部类

    其中 1、2是代表这类事物的特征

     其中3、4是初始化类和对象用的

     其中5协助完成2的功能的实现,表现

  2、定义

    内部类:将一个类 A 定义在另一个类 B 里面,里面的那个类 A 就称为 内部类,类 B 则称为 外部类

  3、分类

    根据内部类的所在的位置不同:

    (1)成员内部类:外部类中方法外;

    (2)局部内部类:方法体内(也可以在代码块内)

二、成员内部类

  1、概述及分类

    成员内部类:定义在 类中方法外 的类。

    (1)有 static 修饰的:静态成员内部类,简称为 静态内部类。

    (2)没有static修饰的,非静态成员内部类,简称 成员内部类。

     扩展:什么情况下会用到成员内部类(方法外声明的)?

当描述一个事物时,发现它的内部还有一个完整的结构需要用一个类来描述;
并且发现这内部的结构,如果独立存在是没有意义的,必须在这个外部类中才有意义。而且这个内部结构只为这个外部类服务。

例如:Body身体,发现它内部还有完整的结果,如:心脏,发现心脏单独创建对象没有意义,只有在 Body 对象中才有意义,而且只为 Body 对象服务。

  2、静态成员内部类(静态内部类)

    (1)语法格式:

【修饰符】 class 外部类  【 extends 父类】 【implements 父接口们】{
  【其他修饰符】 static class 内部类 【 extends 父类】 【implements 父接口们】{
   }
}

               注意

        ① 只有成员内部类才能用 static 修饰,其他的外部类,局部内部类等够不可以用 static 修饰。

        ② 外部类,内部类的父类,父接口都没有关系,各是各自的。

    (2)特点

        静态内部类中,可以出现原本类中能够定义的所有的成员

          属性:可以有静态属性和非静态属性;

          方法:可以有静态方法和非静态方法,如果静态内部类是抽象类,还可定义抽象方法;

          构造方法:有参方法、无参方法;

          代码块:可以有静态代码块和非静态代码块;

          内部类:允许,但很少再写内部类(臃肿)

        静态内部类中不能使用外部类的非静态的成员;

       ③ 在外部类中,使用静态内部类,和使用其他的类一样的原则;

           如果使用静态内部类的静态成员,直接“静态内部类名.”

           如果使用静态内部类的非静态成员,直接“静态内部类对象名.”

       ④ 在外部类的外面,使用静态内部类

           如果使用静态内部类的静态成员,直接“类名”

             使用 外部类名.静态内部类名.静态方法;

             使用 import  包.外部类名.静态内部类名;然后在代码中使用 “静态内部类名

       ⑤ 静态内部类不会随着外部类的初始化一起初始化,而是要在使用到这个静态内部类才会初始化;

    (3)总结

        ① 同级的来说静态的不能直接使用非静态的;

        ② 访问一个类的静态成员,用 “类名.” 即可;访问一个类的非静态成员,用 “对象名.” 即可。

        ③ 当使用到这个类时,这个类才会进行初始化;

      Demo:

 package com.java.test;
//import com.java.test.Outer.Inner; public class TestStaticInner {
public static void main(String[] args) {
// 访问内部类的静态方法
// Inner.test();//上面有导包语句,import 包.外部类名.静态内部类名; Outer.Inner.test();//外部类名.静态内部类名.静态方法(不使用导包语句) // 访问内部类的非静态方法
// Inner in = new Inner();//上面有导包语句,import 包.外部类名.静态内部类名;
Outer.Inner in = new Outer.Inner();
in.method(); Outer out = new Outer(); // 内部类不会初始化
out.outMethod(); // 该方法中用到内部类,内部类会进行初始化
}
}
class Outer{
private int i = 1;
private static int j = 2; static{
System.out.println("外部类的静态代码块");
} static class Inner{
static{
System.out.println("静态内部类的代码块");
} public void method(){
System.out.println("静态内部类的非静态方法");
//System.out.println(i);//错误,不能访问非静态的
System.out.println(j);
} public static void test(){
System.out.println("静态内部类的静态方法");
}
}
// 外部类访问内部类
public void outMethod(){
Inner in = new Inner();
in.method();//非静态方法,用对象名.访问 Inner.test();//静态方法,用类名.访问
}
}

  3、非静态成员内部类

    (1)语法格式:

【修饰符】 class 外部类 【 extends 父类】 【implements 父接口们】{
  【其他修饰符】 class 内部类 【 extends 父类】 【implements 父接口们】{
   }
}

    (2)特点

       ① 在非静态内部类中,不能出现任何和 static 有关的声明;

       ② 在非静态内部类中,可以随意访问外部类的所有的成员,包括静态的和非静态的;

       ③ 在外部类的静态成员中,不能使用非静态的成员内部类

       ④ 在外部类的外面使用

          第一步:先创建外部类的对象

          第二步:要么通过外部类的对象,去创建内部类的对象

Outer out = new Outer();
Outer.Inner in = out.new Inner();

              要么通过外部类的对象,去获取内部类的对象

Outer out = new Outer();
Outer.Inner in = out.getInner();

  

     Demo:

 public class TestNonStaticInner {
public static void main(String[] args) {
//Outer.Inner in = new Outer.Inner();//错误的 //在这里使用Inner,因为此时的Inner是Outer的非静态成员,所以需要用到Outer的对象
Outer out = new Outer();
//Outer.Inner in = out.new Inner();
Outer.Inner in = out.getInner();
in.method();
}
}
class Outer{
private int i = 1;
private static int j = 2; class Inner{
public void method(){
System.out.println("非静态内部类的非静态方法");
System.out.println(i);
System.out.println(j);
}
} public static void outTest(){
//Inner in = new Inner();//静态的方法不能访问非静态的成员
}
public void outMethod(){
Inner in = new Inner();
in.method();
} public Inner getInner(){
return new Inner();
}
}

  案例:创建一个外部类,里面有一个抽象内部类,并有一个抽象方法,要求写一个类去继承该内部类。

  代码

 public class Test {
public static void main(String[] args) {
MySub my = new MySub(new Outer());
my.test();
}
}
class Outer{
abstract class Inner{
public abstract void test();
}
}
class MySub extends Outer.Inner{
MySub(Outer out){
out.super();//需要外部类的对象,才能调用非静态内部类的构造器
} @Override
public void test() {
System.out.println("hello");
}
}

    说明:

    ① 使用非静态内部类名时,可以使用:import 包.外部类名.内部类名; 或 外部类名.内部类名

    ② 要调用非静态内部类的构造器,需要用到外部类的对象

    ③ 因为子类的构造器的首行一定要调用父类的构造器,默认调用父类的无参构造(需要借助外部类对象)

    ④ 继承抽象类,要重写抽象的抽象方法

  注意:内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的 .class 文件,但是前面冠以外部类的类名和 $ 符号。如:Person$Heart.class

三、局部内部类

  1、概述

    局部内部类:定义在 类中方法内 的类,就是局部内部类。即只有当前所属的方法才能使用它,出了这个方法外面就不能使用。

    (1)有名字的局部内部类:简称 局部内部类;

    (2)没名字的局部内部类:简称 匿名内部类。

  2、定义格式

    局部内部类定义格式

【修饰符】 class 外部类名称 {
【修饰符】 返回值类型 外部类方法名称(参数列表) {
【修饰符】 class 有名字的局部内部类名称 {
// ...
}
}
}

          说明:如果子类调用的是父类的无参构造,那么()中实参列表不用写;如果子类调用的是父类的有参构造,那么就在()中传入实参列表

     定义一个类的时候,权限修饰符规则:

    (1)外部类: public / (default)

    (2)成员内部类:public / protected / (default) / private

    (3)局部内部类:不加任何修饰符

  3、特点

    (1)局部内部类的修饰符,只能有 abstract 或 final

    (2)该类有作用域

    (3)如果局部内部类在静态方法中,不能使用外部类的非静态成员

    (4)在局部内部类中,可以使用当前局部内部类所在方法的局部变量,但是要求,这个局部变量必须是 final的常量

      在Java8时,如果某个局部变量被局部内部类使用了,会自动添加final变为常量,一旦变为常量,它的值就不能修改了。

      为什么它要这么要求?加final

      避免局部内部类对象被返回到外部类的外面使用时,访问不到这个局部变量,所以要把这个局部变量变为final的常量。(常量在方法区中)

       原因:

        ① new出来的对象在堆内存当中。

        ② 局部变量是跟着方法走的,在栈内存当中。

        ③ 方法运行结束之后,立刻出栈,局部变量就会立刻消失。

        ④ 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。

    Demo:

 public class TestLocalInner {
public static void main(String[] args) {
Outer out = new Outer();
Father in = out.test();//在外部类的外面虽然不能使用局部内部类,但是可以得到它的对象
System.out.println(in.getClass());
in.method();//在这里仍然可以访问到这个a,那么这个a就不能存在栈中,得挪到方法区,变为常量
}
}
abstract class Father{
public abstract void method();
}
class Outer{
private int i = 1;//成员变量,实例变量,非静态成员变量
private static int j = 2;//成员变量,类变量,静态变量 public Father test(){
//Inner in = new Inner(); 现在还没有声明该类,不能使用 final int a = 10;//局部变量==>局部的常量 //局部内部类
class Inner extends Father{
public void method(){
System.out.println(i);
System.out.println(j);
System.out.println(a);
}
} Inner in = new Inner();
in.method(); return in;
} public void method(){
//Inner in = new Inner(); 超过了作用域
} public static void fun(){
//局部内部类
class Inner{
public void method(){
//System.out.println(i);//是因为fun方法是静态的
System.out.println(j);
}
}
}
}

四、匿名内部类【重要】

  1、概述

    匿名内部类:是内部类的简化写法。它的本质是一个 带具体实现的  父类或者父接口的 匿名的 子类对象

    开发中,最常用到的内部类就是匿名内部类了,以接口为例,当你使用一个接口时,都得做如下几步操作:

    (1)定义子类

    (2)重写接口中的方法

    (3)创建子类对象

    (4)调用重写后的方法

    而匿名类就可以把以上四步合成一步。

    前提:匿名内部类必须继承一个父类或者实现一个父接口

  2、格式

    定义格式

new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};

     特殊:声明匿名内部类与创建它的对象是一起完成的,即匿名内部类只有唯一的对象。

  3、使用方式

    以接口为例,匿名内部类的使用。

   定义接口:

 public abstract class FlyAble{
public abstract void fly();
}

          创建匿名内部类,并调用:

 public class InnerDemo {
public static void main(String[] args) {
/*
1.等号右边:是匿名内部类,定义并创建该接口的子类对象
2.等号左边:是多态赋值,接口类型引用指向子类对象
*/
FlyAble f = new FlyAble(){
  public void fly() {
  System.out.println("我飞了~~~");
  }
};
//调用 fly方法,执行重写后的方法
f.fly();
}
}

  通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。如下所示

 public class InnerDemo2 {
public static void main(String[] args) {
/*
1.等号右边:定义并创建该接口的子类对象
2.等号左边:是多态,接口类型引用指向子类对象
*/
FlyAble f = new FlyAble(){
  public void fly() {
  System.out.println("我飞了~~~");
  }
};
// 将f传递给showFly方法中
showFly(f);
}
public static void showFly(FlyAble f) {
f.fly();
}
}

  将以上两步,也可以简化为一步:

 public class InnerDemo3 {
public static void main(String[] args) {
/*
创建匿名内部类,直接传递给showFly(FlyAble f)
*/
showFly( new FlyAble(){
public void fly() {
System.out.println("我飞了~~~");
}
});
}
public static void showFly(FlyAble f) {
f.fly();
}
}

最新文章

  1. Atitit.log日志技术的最佳实践attilax总结
  2. Java暗箱操作之enum
  3. Windows下 VM12虚拟机安装OS X 10.11 和VM TOOLS
  4. Javascript 中的闭包和引用
  5. RDP setting group policy
  6. MYSQL之性能优化 ----MySQL性能优化必备25条
  7. Android MediaStore与Media.EXTERNAL_CONTENT_URI
  8. .NET+Oracle 分页
  9. 微信开发python+django两个月的成功经历,django是个好框架!
  10. Upgrade to Python 2.7.9 on CentOS5.5
  11. document.write 向文档中写内容,包括文本、脚本、元素之类的,但是它在什么时候执行不会覆盖当前页面内容尼?
  12. POJ3260:The Fewest Coins(混合背包)
  13. HCE:Host-based Card Emulation基于Android设备的卡片模拟器
  14. 【一天一道LeetCode】#46. Permutations
  15. 【软工作业&思考】关于软工的一些概念性理解暨第一次阅读作业
  16. Android Studio 杀掉当前进程
  17. Java知多少(44)异常类型
  18. C# 图像处理:获取鼠标位置信息(全局)
  19. 基于内容的推荐 java实现
  20. 2018.08.16 POJ1183反正切函数的应用(简单数学)

热门文章

  1. 【oracle】定时任务
  2. Hadoop集群上搭建Ranger
  3. jvm参数设置实例
  4. Gamma阶段发布说明
  5. dnsperf
  6. es6 Decorator修饰器
  7. HashMap源码2
  8. Python3+Robot Framework+RIDE安装使用教程
  9. PowerBuilder学习笔记之2PowerScript语言(一)
  10. asp.net core 日志记录到elk