这篇文章主要讲述Java 内部类的相关知识,主要讲解下面的知识点。

  1. 内部类的概念
  2. 内部类的特点与使用
  3. 多种形式内部类
  4. 为什么要使用内部类

内部类的概念

内部类是指在一个类的内部定义了另一个类。例如下面的代码中例子,就是一个简单的内部类。

public class A {
private int a; public class B{
private int b;
}
}

在这个类中,我们可以看出内部类B就像A的成员一样。所以我们对内部类的修饰也可以用很多种修饰符,比如我们是不可能对一个非内部类用private修饰符和protected修饰符,但是如果这个类是内部类,我们可以对他用private和protected访问修饰符以及static和final修饰符等等,就像对一个成员添加修饰符一样。当我们对B类添加多种修饰符以后,我们就可以把B类看成隐藏在A类中,像是一种代码隐藏机制。这样的内部类有什么特点以及我们如何去使用他呢?

内部类的特点与使用

在上面的例子中,我们把B类看成是A类的一个成员,那么这个成员与普通的成员变量和成员方法有什么区别呢? 我们可以很容易的在B类访问到A类中任何成员。但是如果我们想在A类中去访问B类的成员就不这么轻松了。如果B类是一个非静态类,那我们就要在外部类中声明一个B类的引用去调用B类中的成员。如下例子所示:

public class A {
private int a; public B getB(){
return new B();
} public void show(){
System.out.println(getB().b);
}
protected class B{
public int b; public void print(){
System.out.println(a);
}
} public static void main(String[] args) {
new A().show();
}
}

在这个例子中,我们可以看到B类中,是很随意就能访问到A类中的private成员变量a,但是A类中要访问B类中的public成员变量b也是要先定义一个B类的对象,通过这个对象去访问b。这就是内部类一个典型的特点。外部类的一切对内部类都是透明的,但是内部类确可以对外部类隐藏实现的细节。造成这样的原因是因为当某个外部类对象创建一个内部类对象的时候,此内部类的对象必定会秘密的捕获一个指向外部类的引用。这些都是编译器帮你做的。但是如果B类用static修饰也就是变成一个静态类,那么B类中也只能访问A类中的静态成员。

注意,如果上面的show()方法,如果show()是一个静态的方法,那么就不能直接new B()一个对象而是要用a的对象去创建B的对象。

那如果在B类中使用this指的是哪个对象的?按照this的定义显然指向的当前对象B,如果我们要在B类中显式调用A类的对象,我们就要用到一个特殊的语法 . this 显式调用A的对象就是A.this

那如果我们要在一个其他类中的创建B类对象呢?那我们需要用到一个新的语法.new。代码如下:

  A a = new A();
A.B b = a.new B();

这样就在其他类中创建了一个B的对象。在A类中就不需要这样麻烦了,因为会默认省略一个this,所以看起来只是new B()这样简单。

当然,前面说了B类是可以用static修饰,这个时候情况就变了。首先在其他类中创建一个B的对象,我们就不能用A的对象去创建B了 因为B是一个静态类不能用对象去引用,这是我们可以用如下代码去创建B的对象

 A a = new A();
A.B b = new A.B();

或者直接用A.B去调用B类中的静态方法。A.B表示访问A类中的B类

其次,如果我们在A类中写一个静态方法,那么就不需要去A.B,直接用B去调用B类中静态成员。代码如下:

public class A {
public static class B{
public int a = 1;
public int bb;
public static String print(){
return "1";
}
} public static void main(String[] args) {
B.print();
}
}

注意一点,只有B类是静态类才能由静态成员。

多种形式的内部类

我们上面已经了解到了内部类的特点以及如何使用内部类,接下来我们了解一些内部类的类型,Java语言并没有规定内部类必须出现在哪里。所以内部类可以出现在方法内部,出现在作用域中,还可能还是匿名内部类.

方法内部的内部类——局部内部类

我们可以在方法内部定义一个类,这在一定时候是有必要的。这个时候类不能有修饰符。例子如下:

public class A {
public C getC(){
class B extends C{
@Override
public void show() {
System.out.println("我是B");
}
}
return new B();
}
public static void main(String[] args) {
A a = new A();
a.getC().show();
}
}
abstract class C{
public abstract void show();
}

作用域中的内部类

这里指的是内部类出现在方法中的一个作用域内,这样说可能是难懂,比如方法中有一个if语句,内部类写在这个if语句中,这就是内部类出现在一个作用域内。虽然这不常见,但是Java语言是支持这种语法的。

public class A {

    public C getC(boolean flag) {
if (flag) {
class B extends C { @Override
public void show() {
System.out.println("我是B");
}
} return new B();
}
return null;
}
public static void main(String[] args) {
A a = new A();
a.getC(true).show();
}
} abstract class C{
public abstract void show();
}

这个例子,不代表B类的生成是有条件的,相反,B类和A类在编译的时候就一起生成了。

匿名内部类

接下来,我们改写一下局部内部类的例子,让他看上去是这样的。

public class A {

    public C getC() {
return new C() {
@Override
public void show() {
System.out.println("我的匿名内部类");
}
};
}
public static void main(String[] args) {
A a = new A();
a.getC().show();
}
} abstract class C{
public abstract void show();
}

这样看上去就简洁很多,也怪异一些。返回值的生成与表示这个返回值的类的定义结合在一起。这个类的定义还没有名字。这个语法就叫做匿名内部类。

创建一个继承自C的匿名类的对象。通过new表达式返回的引用被自动向上转型为对C的引用。

匿名内部类,主要的功能就是创建一个类的子类,然后复写父类中的方法,然后将子类的对象作为返回值返回。这个常见于接口的回调,也就是C类不在是抽象类而是一个接口。我们无法返回接口的对象,所以只能返回实现了接口的类的对象,这时用匿名内部类的形式来表示这个类的对象是再好不过了。但是这也有缺点,因为匿名内部类无法第二次使用。我们需要不止一个该类的对象的时候,就很尴尬了。我们接下来继续了解匿名内部类的特点,当了解完我们就清楚什么时候该用匿名内部类啦。

上面的代码中是通过默认的构造器来new一个C的对象,我们如果需要一个有参数的构造器来创建C对象,我们就可以直接C类中定义一个有参数的构造器,通过new C的时候直接把参数传入。

public class A {

    public C getC() {
return new C(2) {
@Override
public void show() {
System.out.println(a);
System.out.println("我的匿名内部类");
}
};
}
public static void main(String[] args) {
A a = new A();
a.getC().show();
}
}
abstract class C{
int a;
public C(int a){
this.a = a;
}
public abstract void show();
}

有的时候我们的匿名内部类所在的方法需要一个形参,而这个形参则在匿名内部类中被使用,这个时候我们需要指定这个形参为final类型。直接看这个简单的例子就可以。

public class OuterClass {
public void display(final String name,String age){
class InnerClass{
void display(){
System.out.println(name);
}
}
}
}

这种一定要加final的原因是因为内部类和外部类完全是两个不同的类,也就是说我们在内部类改变了形参的东西,但是对于外部类而言,这个形参没有改变,因为这个形参对于内部类和外部类是两个东西。但是对于程序员而言这就是一个参数,所以为了避免这种尴尬的事情,就加上final来使形参不发生改变。还有另一种解释,就是加上final 形参,则表示给了内部类一个该形参的副本,让内部类可以对他进行任意操作。

有的时候,如果我们要给我们的匿名内部类加一个构造器来进行一些初始化的时候,就很尴尬了,因为我们并不能添加构造器,这时可以用初始化块的形式来进行一些初始化,但是要注意一点,初始化块不能是静态的,而且匿名内部类也不能用静态的成员。

所以到这里,我们也可以发现不用匿名内部类的一个理由,当我们需要一个有名字的构造器的时候,我们就不能用匿名内部类。

为什么使用内部类

如果只是需要实现一个接口或者继承一个抽象类那么直接用外部类就可以了,不需要内部类,但是如果我们需要多继承呢? 内部类最引人的地方就是

每个内部类都能独立地继承一个类或者一个接口的实现,所以无论外部类是否已经继承了类或者实现了一个接口,都不影响内部类。

所以内部类可以是多继承变得更加完整。而且使用内部类还会有更多新特性。

  1. 内部类可以有多个实例,每个实例都有自己的状态。与外部类互相独立。
  2. 在一个外部类里可以写多个内部类去实现同一个接口或者继承同一个类而有不同的实现。
  3. 在外部类中创建内部类的对象,并不依赖于外部类对象的创建。
  4. 在基于回调的事件的时候,匿名内部类是个很好的选择。

Java 浅析内部类的更多相关文章

  1. Java 浅析三大特性之一继承

    上文Java 浅析三大特性之一封装我们说到Java是一个注重编写类,注重于代码和功能复用的语言.Java实现代码复用的方式有很多,这里介绍一个重要的复用方式--继承. 在介绍继承之前,我们要明确一点, ...

  2. Java学习--内部类(一)

    Java学习--内部类(一) 一. 内部类的定义和特点 class Outer{ privite int num = 5; class Inner{ public void Display(){ Sy ...

  3. Java的内部类

    Java的内部类 首先我们来了解一下什么是内部类? 内部类是指在一个外部类的内部再定义一个类.内部类可以是静态static的,也可用public,default,protected和private修饰 ...

  4. java使用内部类的好处及其初始化

    java使用内部类的原因 每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响          java内部类初始化 ForeCatal ...

  5. JAVA基础——内部类详解

    JAVA内部类详解 在我的另一篇java三大特性的封装中讲到java内部类的简单概要,这里将详细深入了解java内部类的使用和应用. 我们知道内部类可分为以下几种: 成员内部类 静态内部类 方法内部类 ...

  6. JAVA面向对象-----内部类的概述

    JAVA面向对象-–内部类的概述s 将类定义在另一个类的内部则成为内部类.其实就是类定义的位置发生了变化. 在一个类中,定义在类中的叫成员变量,定义在函数中的叫成员函数,那么根据类定义的位置也可以分为 ...

  7. Java的内部类真的那么难以理解?

    01 前言 昨天晚上,我把车停好以后就回家了.回家后才发现手机落在车里面了,但外面太冷,冷到骨头都能感受到寒意——实在是不想返回一趟去取了(小区的安保还不错,不用担心被砸车玻璃),于是打定主意过几个小 ...

  8. I/O模型之四:Java 浅析I/O模型(BIO、NIO、AIO、Reactor、Proactor)

    目录: <I/O模型之一:Unix的五种I/O模型> <I/O模型之二:Linux IO模式及 select.poll.epoll详解> <I/O模型之三:两种高性能 I ...

  9. 第30节:Java基础-内部类

    内部类 // 外部类 class Demo{ private int num = 3; // 定义内部类 class Int{ void show(){ System.out.println(&quo ...

随机推荐

  1. python学习 1基础

    对象的等于只是对于值而言 函数定义没有变量提升 常用对象 list []: 列表, 排序省空间 tuple (): 元组,一旦初始化不可修改 dict {}: 字典,方便查询 set {}:集合, 值 ...

  2. 使用canal分析binlog(一) 入门

    canal介绍 canal是应阿里巴巴存在杭州和美国的双机房部署,存在跨机房同步的业务需求而提出的.早期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求.不过早期的数据库同步 ...

  3. MyXls导出Excel的各种设置

    MyXls是一个操作Excel的开源类库,支持设置字体.列宽.行高(由BOSSMA实现).合并单元格.边框.背景颜色.数据类型.自动换行.对齐方式等,通过众多项目的使用表现,证明MyXls对于创建简单 ...

  4. HTML5 与 CSS3 jQuery部分知识总结【转】

    一.    HTML5 为什么需要HTML5 什么是HTML5 HTML5现状及浏览器支持 HTML5优点与缺点 HTML5语法规则与文档声明 HTML5新增表达标签 HTML5多媒体组件 HTML5 ...

  5. 用.net在画出镂空图片

    最近的一个项目需要用到这个东西,冥思苦想了好几天.还是在同事的帮助下,完成此项难题,希望能够帮助以后的博友们 ! 废话不多说,先看看效果图吧. 首先写一下讲一下思路,首先画一张图,当你的背景,然后在图 ...

  6. fsockopen读取、发送cookie及注意事项 -代码示例

    function httpPost($url, $data,$cookieStr='') { $url_array = parse_url($url); $host = $url_array['hos ...

  7. 【hihoCoder】1036 Trie图

    题目:http://hihocoder.com/problemset/problem/1036 给一个词典dict,词典中包含了一些单词words.要求判断给定的一个文本串text中是否包含这个字典中 ...

  8. spring hibernate4 c3p0连接池配置

    c3p0-0.9.1.2.jar,c3p0-oracle-thin-extras-0.9.1.2.jar,点此下载 <bean id="dataSource" class=& ...

  9. SQL Server 2012基本知识

    1:图形化界面设置外键 解决:table->选中表->design->选中需要设置外键的字段->选择"关系"->选择"添加"-&g ...

  10. 利用canvas图片剪切

    <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head> <met ...