java 内部类 嵌套类
概述
java允许我们把一个类a定义在另一个类b里面,那么这个类a就叫做内部类。例如如下面:
- 如果内部类似Non-static的那么被称作内部类
class OuterClass {
...
class NestedClass {
...
}
}
- 如果内部类是static 的那么被称作嵌套类或者内部静态类
class OuterClass {
...
static class StaticNestedClass {
...
}
}
使用方法
- 普通的内部类
Outer o = new Outer();
Outer.Inner i = o.new Inner();
- 内部类的对象只能在与其外部类相关联的情况下才能被创建。
- 内部类访问外部类实例时可以使用Outer.this,创建内部类实例时使用外部类的实例对象 .new Inner()
- 内部类对象隐含地保存了一个指向外部类对象的引用,每一个非静态内部类的实例都链接到一个外部类的实例上
- 静态嵌套类
Outer.Inner i = new Outer.Inner();
- 如果不需要内部类与其外部类对象之间有联系,可以将内部类声明为static.
- 不能访问非静态的外围成员
注意
- 如果一个类要被声明为static的,只有一种情况,就是静态内部类。
- 静态内部类跟静态方法一样,只能访问静态的成员变量和方法,不能访问非静态的方法和属性,但是普通内部类可以访问任意外部类的成员变量和方法
- 静态内部类可以声明普通成员变量和方法以及static成员变量和方法,而普通内部类不能声明static成员变量和方法。
为什么需要内部类
内部类的使用场景
- 先定义一个抽象类
/**
* Selector迭代器
*/
public abstract class Selector<T> {
public void ff(){
System.out.println(this.getClass().getName());
}
abstract boolean hasNext();
abstract T next();
}
再定义一个Sequence类
/**
* 用类包装一个Integer的数组,并实现添加 通过继承Selector迭代遍历
*/
public class Sequence extends Selector<Integer>{
private Object [] items;
private int next =0;
public Sequence(int size){
items=new Object[size];
}
public void add(int x){
if(next <items.length){
items[next]=x;
next++;
}
}
/************************************实现抽象类*/
private int index=0;
@Override
boolean hasNext() {
return !(index==items.length);
}
@Override
Integer next() {
Integer value=null;
if( hasNext()){
value=Integer.parseInt(items[index].toString()) ;
index++;
}
return value;
}
}
我们定义一个Sequence类来包装了一个Object的数组,里面封装了添加初始化操作,通过继承了Selector<T> 来实现了迭代遍历功能。 这时如果我们需要Sequence再继承其他类怎么办?比如现在有一个类SequenceBiz
/**
* Sequence需要继承的业务
*/
public class SequenceBiz {
public void log()
{
//dosomestring 一些需要Sequence继承的业务
System.out.println(this.getClass().getName()+"我记录了日志");
}
}
这个时候可以使用内部类来解决
- 使用内部类
/**
* 用类包装一个Object的数组,并使用内部类通过继承Selector迭代遍历
* 继承SequenceBiz 来处理其他业务
*/
public class Sequence1 extends SequenceBiz{
private Object [] items;
private int next =0;
public Sequence1(int size){
items=new Object[size];
}
public void add(int x){
if(next <items.length){
items[next]=x;
next++;
}
}
private class SequenceSelector extends Selector<Integer>{
private int index=0;
@Override
public boolean hasNext() {
return !(index==items.length);
}
@Override
public Integer next() {
Integer value=null;
if( hasNext()){
value=Integer.parseInt(items[index].toString()) ;
index++;
}
return value;
}
}
/**
* 返回迭代器
* @return
*/
public Selector getSelector()
{
return new SequenceSelector();
}
}
我们来测试一下
public class TestInnerClass {
public static void main(String[] args)
{
// Sequence sequence=new Sequence(5);
// for (int i=0;i<5;i++){
// sequence.add(i);
// }
// while (sequence.hasNext()){
// System.out.println(sequence.next());
// }
Sequence1 sequence1=new Sequence1(5);
for (int i=0;i<5;i++){
sequence1.add(i);
}
Selector selector=sequence1.getSelector();
while (selector.hasNext()){
System.out.println(selector.next());
}
sequence1.log();
}
}
我们来看内部类的好处:
- 使用内部类解决了java中不能继承多个类的问题
- Sequence1创建的private内部类来实现Selector 隐藏了Selector的具体实现。(jdk源码中ArrayList的迭代器就是这种方法)
- 嵌套小类会使代码更靠近使用位置,使代码更加利于维护、
静态内部类的场景
- 普通类中创建一个静态类
public class Outer {
private String name;
private int age;
public static class Builder {
private String name;
private int age;
public Builder(int age) {
this.age = age;
}
public Builder withName(String name) {
this.name = name;
return this;
}
public Builder withAge(int age) {
this.age = age;
return this;
}
public Outer build() {
return new Outer(this);
}
}
private Outer(Builder b) {
this.age = b.age;
this.name = b.name;
}
}
静态内部类调用外部类的构造函数,来构造外部类,由于静态内部类可以被单独初始化说有在外部就有以下实现。即Builder design pattern(生成器设计模式) 我们可以在main中使用
调用 Outer outer = new Outer.Builder(2).withName("Yang Liu").build();
- 总结
- 如果类的构造器或者静态工厂中有多个参数,设计这样类时,最好使用builder模式,特别是大多数参数都是可选的时候。
- 如果现在不能确定参数的个数,使用构造器即builder模式。
- 静态内部类提高了封装性,和代码的可读性。
- 接口中的内部类 在接口中我们定义的内部类只能是静态内部类。关于使用场景可以参考shiro框架中Subject接口。
/**
*生成器设计模式实现用于以简化的方式创建实例
Builder design pattern implementation for creating {@link Subject} instances in a simplified way without requiring knowledge of Shiro's construction techniques.
*
/
public interface Subject {
public static class Builder {
}
}
总结
- 使用内部类解决了多继承的问题
- 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
- 使代码更靠近使用位置,使代码更加利于维护