Java内部类(超详细)

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

属性、方法、构造器、代码块、内部类

内部类就是在一个类或方法中定义的类。内部类又分为成员内部类,静态内部类,匿名内部类和局部内部类。

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());
    }
}
  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个级复杂的 Java 成绩排名程序: ```java import java.util.*; public class ScoreRanking { public static void main(String[] args) { // 初始化学生信息 Student[] students = new Student[5]; students[0] = new Student("张三", 80, 90, 70); students[1] = new Student("李四", 90, 70, 80); students[2] = new Student("王五", 70, 80, 90); students[3] = new Student("赵六", 85, 75, 82); students[4] = new Student("刘七", 95, 85, 92); // 计算每个学生的总分和平均分 for (int i = 0; i < students.length; i++) { students[i].computeTotalScore(); students[i].computeAverageScore(); } // 按照总分排序 Arrays.sort(students, new Comparator<Student>() { public int compare(Student s1, Student s2) { return s2.getTotalScore() - s1.getTotalScore(); } }); // 输出排名结果 System.out.println("排名\t姓名\t语文\t数学\t英语\t总分\t平均分"); for (int i = 0; i < students.length; i++) { System.out.println((i + 1) + "\t" + students[i].getName() + "\t" + students[i].getChineseScore() + "\t" + students[i].getMathScore() + "\t" + students[i].getEnglishScore() + "\t" + students[i].getTotalScore() + "\t" + students[i].getAverageScore()); } } } class Student { private String name; private int chineseScore; private int mathScore; private int englishScore; private int totalScore; private double averageScore; public Student(String name, int chineseScore, int mathScore, int englishScore) { this.name = name; this.chineseScore = chineseScore; this.mathScore = mathScore; this.englishScore = englishScore; } public void computeTotalScore() { this.totalScore = chineseScore + mathScore + englishScore; } public void computeAverageScore() { this.averageScore = totalScore / 3.0; } public String getName() { return name; } public int getChineseScore() { return chineseScore; } public int getMathScore() { return mathScore; } public int getEnglishScore() { return englishScore; } public int getTotalScore() { return totalScore; } public double getAverageScore() { return averageScore; } } ``` 这个程序定义了一个 `Student` 类来表示学生,包括姓名和语文、数学、英语三门成绩。它还定义了两个方法 `computeTotalScore` 和 `computeAverageScore` 来计算总分和平均分。 在 `ScoreRanking` 类的 `main` 方法中,我们首先初始化了五个学生的信息,然后计算每个学生的总分和平均分。接着,我们使用 `Arrays.sort` 方法按照总分排序,最后输出排名结果。 值得注意的是,我们使用了一个匿名内部类来定义排序规则,这个内部类实现了 `Comparator` 接口的 `compare` 方法,用于比较两个学生的总分大小。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值