上一篇我们学习了接口还有访问控制,在以后的工作中接口是我们经常要碰到的,所以一定要多去回顾。接下来介绍一下内部类。很多时候我们创建类的对象的时候并不需要使用很多次,每次只使用一次

这个时候我们就可以使用内部类了。

一、内部类概述

  内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。

  内部类不是在一个java源文件中编写俩个平行的俩个类,而是在一个类的内部再定义另外一个类。 我们可以把外边的类称为外部类,在其内部编写的类称为内部类。

  内部类分为四种:

      成员内部类

      静态内部类

      局部内部类

      匿名内部类

二、成员内部类(实例内部类、非静态内部类)

  注:成员内部类中不能写静态属性和方法

  1.1、定义一个内部类  

 /在A类中申明了一个B类,此B类就在A的内部,并且在成员变量的位置上,所以就称为成员内部类
public class Outer {
private int id;
public void out(){
System.out.println("这是外部类方法");
} class Inner{
public void in(){
System.out.println("这是内部类方法");
}
}
}

  1.2、实例化内部类

  实例化内部类,首先需要实例化外部类,通过外部类去调用内部类  

 public class Outer {
private int id;
public void out(){
System.out.println("这是外部类方法");
} class Inner{
public void in(){
System.out.println("这是内部类方法");
}
}
} ----------------------------------------------------------------
public class Test{
public static void main(String[] args) {
//实例化成员内部类分两步
//1、实例化外部类
Outer outObject = new Outer();
//2、通过外部类调用内部类
Outer.Inner inObject = outObject.new Inner();
     //测试,调用内部类中的方法
     inObject.in();//打印:这是内部类方法
}

  分析:想想如果你要使用一个类中方法或者属性,你就必须要先有该类的一个对象,同理,一个类在另一个类的内部,那么想要使用这个内部类,就必须先要有外部类的一个实例对象,然后在通过该对象去使用内部类。

  1.3、成员内部类能干什么?

  1)·访问外部类的所有属性(这里的属性包括私有的成员变量,方法)  

 public class Outer {
private int id;
public void out(){
System.out.println("这是外部类方法");
} class Inner{
public void in(){
System.out.println("这是内部类方法");
}
     
     //内部类访问外部类私有的成员变量
     public void useId(){
       System.out.println(id+3);。
     }      //内部类访问外部类的方法 
     public void useOut(){
       out(); 
     } 
}
}
-------------------------------------------------- public class Test{
public static void main(String[] args) {
//实例化成员内部类分两步
//1、实例化外部类
Outer outObject = new Outer();
//2、通过外部类调用内部类
Outer.Inner inObject = outObject.new Inner();
     //测试
     inObject.useId();//打印3,因为id初始化值为0,0+3就为3,其中在内部类就使用了外部类的私有成员变量id。
     inObject.useOut();//打印:这是外部类方法
}
}

  2)如果内部类中的变量名和外部类的成员变量名一样,要通过创建外部类对象 "."属性来访问外部类属性,通过this.属性访问内部类成员属性

 public class Outer {
private int id;//默认初始化0
public void out(){
System.out.println("这是外部类方法");
} class Inner{
private int id=8; //这个id跟外部类的属性id名称一样。
public void in(){
System.out.println("这是内部类方法");
} public void test(){
System.out.println(id);//输出8,内部类中的变量会暂时将外部类的成员变量给隐藏
//如何调用外部类的成员变量呢?通过Outer.this,想要知道为什么能通过这个来调用,就得明白下面这个原理
//想实例化内部类对象,就必须通过外部类对象,当外部类对象来new出内部类对象时,会
//把自己(外部类对象)的引用传到了内部类中,所以内部类就可以通过Outer.this来访问外部类的属性和方法,到这里,你也就可以知道为什么内部类可以访问外部类的属性和方法,这里由于有两个相同的
属性名称,所以需要显示的用Outer.this来调用外部类的属性,平常如果属性名不重复,那么我们在内部类中调用外部类的属性和方法时,前面就隐式的调用了Outer.this。 System.out.println(Outer.this.id);//输出外部类的属性id。也就是输出0
}
}
}

  借助成员内部类,来总结内部类(包括4种内部类)的通用用法:

    1、要想访问内部类中的内容,必须通过外部类对象来实例化内部类。

    2、能够访问外部类所有的属性和方法,原理就是在通过外部类对象实例化内部类对象时,外部类对象把自己的引用传进了内部类,使内部类可以用通过Outer.this去调用外部类的属性和方法,

      一般都是隐式调用了,但是当内部类中有属性或者方法名和外部类中的属性或方法名相同的时候,就需要通过显式调用Outer.this了。

  1.4、写的一个小例子

 package com.zyh.inner;

 public class MemberInnerClassTest {
private String name;
private static int age; public void run(){} public static void go(){} public class MemberInnerClass{
private String name; //内部类访问外部类
public void test(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(MemberInnerClassTest.this.name);
System.out.println(MemberInnerClassTest.age);
MemberInnerClassTest.this.run();
MemberInnerClassTest.go();
}
} //外部类访问成员内部类
//成员内部类的对象要 依赖于外部类的对象的存在
public void test(){
//MemberInnerClass mic = MemberInnerClassTest.this.new MemberInnerClass();
//MemberInnerClass mic = this.new MemberInnerClass();
MemberInnerClass mic = new MemberInnerClass();
mic.name = "tom";
mic.test("hua"); } public static void main(String[] args) {
//MemberInnerClass mic = new MemberInnerClass();这个是不行的,this是动态的。
//所以要使用要先创建外部类对象,才能使用
MemberInnerClassTest out = new MemberInnerClassTest();
MemberInnerClass mic = out.new MemberInnerClass();
//如果内部类是private,则不能访问,只能铜鼓内部方法来调用内部类
mic.name="jik";
mic.test("kkk"); } }

三、静态内部类

  看到名字就知道,使用你static修饰的内部类就叫静态内部类。

  既然提到了static,那我们就来复习一下它的用法:一般只修饰变量和方法,平常不可以修饰类,但是内部类却可以被static修饰。

          1)static修饰成员变量:整个类的实例共享静态变量

          2)static修饰方法:静态方法,只能够访问用static修饰的属性或方法,而非静态方法可以访问static修饰的方法或属性

          3)被static修饰了的成员变量和方法能直接被类名调用。

          4)static不能修饰局部变量,切记,不要搞混淆了,static平常就用来修饰成员变量和方法。

  写了一个例子,可以给大家看一下:

 public class StaticInnerClassTest {
private String name;
private static int age; public void run(){} public static void go(){} //外部类访问静态内部类
public void test(){
StaticInnerClass sic = new StaticInnerClass(); //静态的内部类不需要依赖外部类,所以不用this
sic.name = "tom";
sic.test1("jack"); StaticInnerClass.age=10;
StaticInnerClass.test2("xixi"); } private static class StaticInnerClass{
private String name;
private static int age;
public void test1(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(StaticInnerClass.age); System.out.println(StaticInnerClassTest.age);
//System.out.println(StaticInnerClassTest.this.name);静态类不能访问非静态属性 StaticInnerClassTest.go();
//StaticInnerClassTest.this.run();静态类不能访问非静态方法
}
public static void test2(String name){
//只能访问自己和外部类的静态属性和方法
System.out.println(name);
//System.out.println(this.name);静态方法里面连自己类的非静态属性都不能访问
System.out.println(StaticInnerClass.age); System.out.println(StaticInnerClassTest.age);
//System.out.println(StaticInnerClassTest.this.name);静态方法不能访问非静态属性
StaticInnerClassTest.go();
//StaticInnerClassTest.this.run();静态方法不能访问非静态方法
} }
}

例子

  注意:

        1、我们上面说的内部类能够调用外部类的方法和属性,在静态内部类中就行了,因为静态内部类没有了指向外部类对象的引用。除非外部类中的方法或者属性也是静态的。这就回归到了static关键字的用法。

        2、静态内部类能够直接被外部类给实例化,不需要使用外部类对象

              Outer.Inner inner = new Outer.Inner();

        3、静态内部类中可以声明静态方法和静态变量,但是非静态内部类中就不可以声明静态方法和静态变量

四、局部内部类

   局部内部类是在一个方法内部声明的一个类
        局部内部类中可以访问外部类的成员变量及方法
        局部内部类中如果要访问该内部类所在方法中的局部变量,那么这个局部变量就必须是final修饰的

 public class Outer {
private int id;
  //在method01方法中有一个Inner内部类,这个内部类就称为局部内部类
public void method01(){class Inner{
public void in(){
System.out.println("这是局部内部类");
}
}
}
}

定义一个局部内部类

  局部内部类一般的作用跟在成员内部类中总结的差不多,但是有两个要注意的地方:

  1)在局部内部类中,如果要访问局部变量,那么该局部变量要用final修饰   

    为什么需要使用final?

     final修饰变量:变为常量,会在常量池中放着,

     逆向思维想这个问题,如果不实用final修饰,当局部内部类被实例化后,方法弹栈,局部变量随着跟着消失,这个时候局部内部类对象在想去调用该局部变量,就会报错,因为该局部变量已经没了

    ,当局部变量用fanal修饰后,就会将其加入常量池中,即使方法弹栈了,该局部变量还在常量池中呆着,局部内部类也就是够调用。所以局部内部类想要调用局部变量时,需要使用final修饰,不使用,编译度通不过。

 public class Outer {
private int id;
public void method01(){
final int cid = 3; //这个就是局部变量cid。要让局部内部类使用,就得变为final并且赋值,如果不使用final修饰,就会报错
class Inner{
//内部类的第一个方法
public void in(){
System.out.println("这是局部内部类");
}
//内部类中的使用局部变量cid的方法
public void useCid(){
System.out.println(cid);
}
} }
}

例子

  2)局部内部类不能通过外部类对象直接实例化,而是在方法中实例化出自己来,然后通过内部类对象调用自己类中的方法。看下面例子就知道如何用了。

 public class Outer {
private int id; public void out(){
System.out.println("外部类方法");
}
public void method01(){
class Inner{
public void in(){
System.out.println("这是局部内部类");
}
}
//关键在这里,如需要在method01方法中自己创建内部类实例,然后调用内部类中的方法,等待外部类调用method01方法,就可以执行到内部类中的方法了。
Inner In = new Inner();
In.in();
}
}

例子

  使用局部内部类需要注意的地方就刚才上面说的:

          1、在局部内部类中,如果要访问局部变量,那么该局部变量要用final修饰

          2、如何调用局部内部类方法。  

 package com.zyh.inner;

 public class LocalInnerClassTest {
private String name;
private static int age; public void run(){} public static void go(){} //局部内部类要定义在方法中
public void test(){
final String myname="";
class LocalInnerClass{
private String name;
// private static int age;不能定义静态属性 public void test(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(myname);
System.out.println(LocalInnerClassTest.this.name); LocalInnerClassTest.this.run();
LocalInnerClassTest.go();
}
}
//局部内部类只能在自己的方法中用,因为局部内部类相当于一个局部变量,除了方法就找不到了。
LocalInnerClass lic = new LocalInnerClass();
lic.name="tom";
lic.test("test");
} }

自己写了一个例子

四、匿名内部类

  在这四种内部类中,以后的工作可能遇到最多的是匿名内部类,所以说匿名内部类是最常用的一种内部类。

  什么是匿名对象?如果一个对象只要使用一次,那么我们就是需要new Object().method()。 就可以了,而不需要给这个实例保存到该类型变量中去。这就是匿名对象。  

 public class Test {
public static void main(String[] args) {
//讲new出来的Apple实例赋给apple变量保存起来,但是我们只需要用一次,就可以这样写
Apple apple = new Apple();
apple.eat();
//这种就叫做匿名对象的使用,不把实例保存到变量中。
new Apple().eat();
}
}
class Apple{
public void eat(){
System.out.println("我要被吃了");
}
}

什么是匿名对象

 匿名内部类跟匿名对象是一个道理,

        匿名对象:我只需要用一次,那么我就不用声明一个该类型变量来保存对象了,

        匿名内部类:我也只需要用一次,那我就不需要在类中先定义一个内部类,而是等待需要用的时候,我就在临时实现这个内部类,因为用次数少,可能就这一次,

              那么这样写内部类,更方便。不然先写出一个内部类的全部实现来,然后就调用它一次,岂不是用完之后就一直将其放在那,那就没必要那样。

   1)匿名内部类需要依托于其他类或者接口来创建
              如果依托的是类,那么创建出来的匿名内部类就默认是这个类的子类
              如果依托的是接口,那么创建出来的匿名内部类就默认是这个接口的实现类。

   2)匿名内部类的声明必须是在使用new关键字的时候
              匿名内部类的声明及创建对象必须一气呵成,并且之后能反复使用,因为没有名字。
              例如:
                  A是一个类(普通类、抽象类都可以)
                  依托于A类创建一个匿名内部类对象
                  main:
                    
                      A a = new A(){
                          //实现A中的抽象方法
                          //或者重写A中的普通方法
                      };
                      注:这个大括号里面其实就是这个内部类的代码,只不过是声明该内部类的同时就是要new创建了其对象,并且不能反复使用,因为没有名字。
            
              例如:
                  B是一个接口
                  依托于B接口创建一个匿名内部类对象
                  B b = new B(){
                      //实现B中的抽象方法
                  };

    3)匿名内部类除了依托的类或接口之外,不能指定继承或者实现其他类或接口,同时也不能被其他类所继承,因为没有名字。

    4)匿名内部中,我们不能写出其构造器,因为没有名字。

    5)匿名内部中,除了重写上面的方法外,一般不会再写其他独有的方法,因为从外部不能直接调用到。(间接是调用到的) 

 public interface Work{
void doWork();
}
public class AnonymousOutterClass{
private String name;
private static int age;
public void say(){}
public static void go(){} public void test(){
final int i = 90; Work w = new Work(){
public void doWork(){
System.out.println(AnonymousOutterClass.this.name);
System.out.println(AnonymousOutterClass.age);
AnonymousOutterClass.this.say();
AnonymousOutterClass.go(); System.out.println(i);
}
};
w.doWork();
}
}

   我们可以是一下不用匿名内部类和用匿名内部类实现一个接口中的方法的区别

 public class Test {
public static void main(String[] args) {
    //如果我们需要使用接口中的方法,我们就需要走3步,1、实现接口 2、创建实现接口类的实例对象 3、通过对象调用方法
      //第二步
     Test02 test = new Test02();
      //第三步
test.method();
}
} //接口Test1
interface Test01{
public void method();
}
//第一步、实现Test01接口
class Test02 implements Test01{ @Override
public void method() {
System.out.println("实现了Test接口的方法");
} }

不用匿名内部类

 public class Test {
public static void main(String[] args) {
      //如果我们需要使用接口中的方法,我们只需要走一步,就是使用匿名内部类,直接将其类的对象创建出来。
    new Test1(){
        public void method(){
          System.out.println("实现了Test接口的方法");
        }
      }.method(); }
} interface Test1{
public void method();
}

使用匿名内部类

  解析:其实只要明白一点,new Test1(){实现接口中方法的代码};  Test1(){...}这个的作用就是将接口给实现了,只不过这里实现该接口的是一个匿名类,也就是说这个类没名字,

     只能使用这一次,我们知道了这是一个类, 将其new出来,就能获得一个实现了Test1接口的类的实例对象,通过该实例对象,就能调用该类中的方法了,因为其匿名类是在一个类中实现的,

     所以叫其匿名内部类,不要纠结为什么Test1(){...}就相当于实现了Test1接口,这其中的原理等足够强大了,在去学习,不要钻牛角尖,这里就仅仅是需要知道他的作用是什么,做了些什么东西就行。

  

 

  

JavaSE(七)之内部类的更多相关文章

  1. JAVA基础之内部类

    JAVA基础之内部类 2017-01-13 1.java中的内部类都有什么?! 成员内部类 局部内部类 匿名内部类 静态内部类 2.内部类详解 •成员内部类 在一个类的内部再创建一个类,成为内部类 1 ...

  2. JavaSE 学习笔记之内部类(九)

    内部类:如果A类需要直接访问B类中的成员,而B类又需要建立A类的对象.这时,为了方便设计和访问,直接将A类定义在B类中.就可以了.A类就称为内部类.内部类可以直接访问外部类中的成员.而外部类想要访问内 ...

  3. Javase之内部类概述

    内部类概述 把类定义在其他类的内部就称为内部类 class A{ class B{ } } B就称为内部类,A称为外部类. 内部类的访问特点 内部类直接访问外部类成员,包括私有. 外部类要访问内部类要 ...

  4. java之内部类(InnerClass)----非静态内部类、静态内部类、局部内部类、匿名内部类

    提起java内裤类(innerClass)很多人不太熟悉,实际上类似的概念在c++里面也有,那就是嵌套类(Nested Class),关于这俩者的区别,在下文中会有对比.内部类从表面上看,就是在类中定 ...

  5. Java 之内部类

    概述 内部类修饰符 内部类的细节 局部内部类 匿名内部类及其应用 匿名内部类细节 内部类概述 将一个类定义在另一个类的里面, 里面的那个类就称为内部类(内置类, 嵌套类). class Outer { ...

  6. Java类成员之内部类

    内部类含义: 在Java中允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类. Inner class 一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称. Inner ...

  7. java之内部类详解

    序言 有位小同学要我写一篇这个的总结,我说那好吧,那就动手写总结一下这个内部类的知识,感觉这个在面试中也会经常遇到,内部类.反射.集合.IO流.异常.多线程.泛型这些重要的基础知识大家都比较容易记不住 ...

  8. Java核心技术点之内部类

    1. 为什么要使用内部类     内部类就是定义在一个类内部的类,那么为什么要使用内部类呢?主要原因有以下几点:第一,内部类中定义的方法能访问到它所在外部类的私有属性及方法:第二,外部类无法实现对同一 ...

  9. 黑马程序员——JAVA基础之内部类,匿名内部类

    ------- android培训.java培训.期待与您交流! ---------- 内部类 将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类). 内部类访问特点: •  内部 ...

随机推荐

  1. 源码安装LNMP环境

    新装CentOS 6.7,安装默认服务版本basic server 安装顺序linux(忽略...)--> Nginx--> Mariadb--> PHP 为了不影响测试效果,首先关 ...

  2. Jquery DataTables 使用AJAX POST的问题

    最近项目在用需要用表格,听说DataTables很好很强大,于是用了一下. Get请求没什么问题,问题处在POST请求上 Jquery原生的POST请求没有问题,代码如下 $.ajax({   url ...

  3. eclipse 和 jdk的位数不同

    如果eclipse 和 jdk的位数不同,如一个64Bit一个32位,不需要修改环境变量,只需要在eclipse根目录的eclipse.ini文件中第一行加入:-vmD:\Program Files\ ...

  4. 通过java反射得到javabean的属性名称和值参考

    通过java反射得到javabean的属性名称和值 Field fields[]=cHis.getClass().getDeclaredFields();//cHis 是实体类名称 String[] ...

  5. 一键将Web应用发布到云-Azure Web App

    我们现在越来越多的传统应用,逐步向云端迁移,原先私有云的部署模式,逐步向云端PaaS IaaS转变.例如: 我们在云端Azure中申请VM虚拟机,将我们的Web应用部署到VM的IIS中,同时做云服务的 ...

  6. Unity3D中使用BMFont制作图片字体 (NGUI版)

    [旧博客转移 - 发布于2015年9月10日 16:07] 有时美术会出这种图片格式的文字,NGUI提供了UIFont来支持BMFont导出的图片字体 BMFont原理其实很简单,首先会把文字小图拼成 ...

  7. 微信公众平台——token验证php版

    这几天开始接触微信公众号的开发,注册这些就不说了,我是先弄了个测试号用着.进入正题 所谓token验证,其实就是微信服务器向自己要用到的服务器url发送一段数据,其中有一个参数$_GET['echho ...

  8. PHP运算符知识点

    表达式 几乎所写的任何东西都是一个表达式,简单却最精确的定义一个表达式的方式就是"任何有值的东西". 算术运算符 Php中常用的有:+.-.*./.%(取模,得到余数) 左+ - ...

  9. jmeter-Java-MongoDB 数据库增删改查操作

    在日常测试过程中会发现有些测试数据是通过数据库来获取的,一般常用的数据比如SQL .Oracle,此类数据库jmeter有专门的插件进行使用JDBC,今天跟大家说一说关于Mongodb这个数据库jme ...

  10. 【一步一步】Spring 源码环境搭建

    平时项目中基本上都会用到spring,但是源码还没有深入的了解过.趁这段时间稍微空闲点,开始研究下spring 源码.下面是spring 源码的环境搭建. 主要分为如下步骤: ①安装jdk,gradl ...