在学习内部类之前,咱们先了解一下,类的五大成员是哪些?

特色、办法、结构器、代码块、内部类

内部类就是在一个类或办法中界说的类。内部类又分为成员内部类,静态内部类,匿名内部类和部分内部类。

1、成员内部类

成员内部类是界说在类的内部,作为类的成员的类。

public class Outer {
   private Inner inner=null;
   private double r;
   String b;
   public Inner getInnerInstance(){//用于回来一个内部类目标
        if (inner==null)
            inner=new Inner();
        return inner;
    }
 private class Inner{//成员内部类,用private润饰,只能外部类的内部拜访
      final static int t=1;//成员内部类不能界说静态成员,final润饰的在外
       public void draw(){
         System.out.println("绘制一个圆,半径为"+r);
        }
   }
}

特色如下:

  1. 内部类能够直接拜访外部类的一切成员(成员变量和成员办法),包括private和static所润饰的。但是外部类不能直接拜访内部类成员,需求通过预先创立的内部类目标去拜访。
  2. 成员内部类能够运用权限润饰符(private、default、protected、public)任意进行润饰。
  3. 成员内部类是默许包括了一个指向外部类目标的引证。要创立成员内部类目标,有必要先创立一个外部类目标。
  4. 成员内部类目标创立办法:
//第一种办法
Outer outer=new Outer();
Outer.Inner inner=outer.new Inner();
//第二种办法
Outer.Inner inner1=outer.getInnerInstance();//在外部类提供一个办法,回来一个内部类目标
  1. 当成员内部类具有和外部类同名的成员变量或许办法时,会产生躲藏现象,即默许情况下拜访的是成员内部类的成员。假如要拜访外部类的同名成员,需求以下面的方式进行拜访:

外部类.this.成员变量/成员办法

  1. 外部类的静态成员不能拜访内部类,内部类不能够界说静态成员(final润饰的在外),比方静态办法、静态特色和静态代码块。

2、静态内部类

运用static润饰的成员内部类咱们称之为静态内部类。

public class Test {
    public static void main(String[] args) {
     Outer.Inner inner=new Outer.Inner();//静态内部类能够被其他类直接拜访和实例化,而不需求先实例化外部类。
     inner.draw();
    }
}
 class Outer {
  int t=0;
  static  String desc="123";
    static class Inner{
        static int x=10;
        {
            System.out.println("这里是静态内部类的代码块");
        }
        public static void draw(){
            System.out.println("t的值:");//这里会编译报错
            System.out.println("desc的值:"+desc);
        }
    }
}

特色如下:

  1. 静态内部类能够拜访外部类的静态成员,不能拜访非静态成员,内部类还能够界说静态成员。
  2. 静态内部类是4品种中仅有一个不依赖于外部类目标的引证的内部类,静态内部类能够被其他类直接拜访和实例化,不需求先实例化外部类。

3、匿名内部类

匿名内部类没有显式的类名,通常在创立目标的时分界说,能够直接在表达式中运用,不需求单独声明一个命名的类。在jdk8新特性中能够运用Lambda表达式代替。

public class Test {
    public static void main(String[] args) {
     Calculator calculator=new Calculator() {// 创立一个匿名内部类完成Calculator接口
            @Override
            public int calculate(int a, int b) {
                return a + b;
            }
        };
        System.out.println(calculator.calculate(2,3));
    }
}
interface Calculator {
    int calculate(int a, int b);
}

匿名内部类能够出现在任何允许表达式出现的地方,比方办法参数、变量初始化、办法回来值等。界说格局:

new 父类结构器(参数列表)或 完成接口()
{
//匿名内部类的类体部分
}

特色

  1. 匿名内部类能够拜访外部类一切的变量和办法,不能在匿名内部类中修正外部部分变量。
  2. 匿名内部类默许包括了外部类目标的引证。
  3. 运用匿名内部类还有个前提条件有必要承继一个父类或完成一个接口
  4. 匿名内部类只能运用一次,它通常用来简化代码编写。

注:匿名内部类是仅有一种没有结构器的类。匿名内部类用于承继其他类或是完成接口,并不需求添加额定的办法,只是对承继办法的完成或是重写。

运用Lambda进行替换

Lambda表达式并不能取代一切的匿名内部类,能够运用Lambda的依据是有必要有相应的函数接口(函数接口,是指内部只要一个笼统办法的接口)。

1.无参函数的简写

假如需求新建一个线程,匿名内部类写法:

new Thread(new Runnable(){// 接口名
	@Override
	public void run(){// 办法名
		System.out.println("Thread run()");
	}
}).start();

Lambda表达式简化写法:

new Thread(
        () -> {     // 省略接口名和办法名
         System.out.print("Hello");  
         System.out.println("Jack"); 
         } 
        ).start();
 //假如函数体只要一行句子,花括号直接去掉,留下那一行句子,比方() -> System.out.println("Thread run()")

2.带参数函数的简写

假如要给一个字符串列表通过自界说比较器,依照字符串长度进行排序,匿名内部类写法:

List<String> list = Arrays.asList("I", "love", "you");
Collections.sort(list, new Comparator<String>(){// 接口名 
      @Override 
      public int compare(String s1, String s2){// 办法名 
      if(s1 == null) return -1; 
      if(s2 == null) return 1; 
      return s1.length()-s2.length(); 
      } 
      });

Lambda表达式简化写法:

List<String> list = Arrays.asList("I", "love", "you");
Collections.sort(list, (s1, s2) ->{// 省略参数表的类型 
if(s1 == null) return -1; 
if(s2 == null) return 1; 
return s1.length()-s2.length(); });

自界说函数接口

自界说函数接口,只需求编写一个只要一个笼统办法的接口即可。

// 自界说函数接口
@FunctionalInterface  //这个注解是可选的,但加上该标示编译器会帮你查看接口是否契合函数接口规范。
interface MyInterface<T>{
    void doSomething(T t);
}
class Test<T>{
    private List<T> list;
    public void myForEach(MyInterface<T> myInterface){
        for (T t:list) {
            myInterface.doSomething(t);
        }
    }
    public static void main(String[] args) {
        Test test=new Test();
        test.list= Arrays.asList(12,13,14,15,16,17);
        test.myForEach(str->System.out.println(str));// 运用自界说函数接口书写Lambda表达式
    }
}

*需求注意的是:*

lambda表达式隐含了return关键字,所以在单个的表达式中,咱们无需显式的写return关键字,
但是当表达式是一个句子集合的时分,则需求显式添加return,并用花括号{ }将多个表达式包围起来,下面看几个比如:

//回来给定字符串的长度,隐含return句子
(String s) -> s.length() 
// 一直回来42的无参办法
() -> 42 
// 包括多行表达式,则用花括号括起来
(int x, int y) -> {
    int z = x * y;
    return x + z;
}

4、部分内部类

部分内部类是界说在一个办法或许一个效果域里面的类,它和成员内部类的区别在于部分内部类的拜访仅限于办法内或许该效果域内。效果域指的是办法里的代码块(如 if/else 句子块、for 循环块、while 循环块等)。

class Outer{
    public void test(){
        int x=20;
        class Inner{ //在办法内界说
            public void print(){
                System.out.println("我今年"+x);
            }
        }
        new Inner().print();  // 部分内部类有必要在办法内部实例化,然后return出去或许直接调用其办法。
    }
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.test();
    }
}

特色如下:

  1. 部分内部类不能有拜访权限润饰符,且不能被界说为static。
  2. 内部类能够直接拜访外部类的一切成员(成员变量和成员办法)。
  3. 部分内部类默许包括了外部类目标的引证
  4. 部分内部类也能够运用Outer.this语法制定拜访外部类成员

5、内部类效果

  • 能够完成多重承继。(最大的优点)
  • 内部类提供了更好的封装,除了该外围类,其他类都不能拜访。
  • 内部类具有外围类的一切元素的拜访权限。
  • 在单个外围类中,能够让多个内部类以不同的办法完成同一个接口,或许承继同一个类。

成员内部类完成多承继:

class Father {
    public int eat(){
        return 10;
    }
}
class Mother {
    public int fly(){
        return 20;
    }
}
 class Son {
    class Father_1 extends Father{
        public int eat(){
            return super.eat() + 10;
        }
    } 
    class Mother_1 extends  Mother{
        public int fly(){
            return super.fly() - 7;
        }
    }  
    public int geteat(){
        return new Father_1().eat();
    }  
    public int getfly(){
        return new Mother_1().fly();
    }
}
public class Test {
    public static void main(String[] args) {
        Son son = new Son();
        System.out.println( son.geteat());
        System.out.println( son.getfly());
    }
}