当前位置:   article > 正文

JavaSE学习总结(七)面向对象(下)抽象类接口接口的默认方法抽象类和接口的区别链式编程成员内部类局部内部类匿名内部类类中定义接口函数式接口Lambda表达式_默认抽象类

默认抽象类

JavaSE学习总结(七)面向对象(下)抽象类/接口/接口的默认方法/抽象类和接口的区别/链式编程/成员内部类/局部内部类/匿名内部类/类中定义接口/函数式接口/Lambda表达式

面向对象(下)

一、抽象类

(一)抽象类概述

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
  在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。

(二)抽象类特点

1.抽象类和抽象方法必须用abstract关键字修饰

抽象类格式:abstract class 类名 {}
抽象方法格式:public abstract void eat();

2.抽象方法没有方法实现体,只给出一个方法声明

抽象类,就是被abstract所修饰的类,父类将所有子类的共性功能向上抽取后,他并不知道,每个子类对这个共性功能的具体实现,所以没有必要在父类中给出共性功能的具体实现,而是给出声明即可,所谓给出功能的声明,就是将此功能抽象出来,然后强制子类必须重写该抽象的功能。

3.抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
4.抽象类中可以有构造方法,但抽象类不能进行实例化,那么要构造方法有什么作用呢?

用于子类访问父类数据时的初始化

5.抽象类不能直接实例化,那么抽象类如何实例化呢?

按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。

6.抽象类的子类

要么是抽象类
要么重写抽象类中的所有抽象方法

案例演示1

public class MyTest {
    public static void main(String[] args) {
        //new Animal();报错:抽象类不能直接创建对象
        //我们可以采用多态间接的去实例化抽象类
        Animal an=new Cat();
        an.eat();
        an.sleep();
        an.show();
    }
}
//一旦一个类中,有了抽象方法,此类必须为抽象类
//一个抽象类中也可以没有抽象方法
//抽象类中既可以有抽象方法,也可以有非抽象方法,抽象方法,强制子类重写,非抽象方法,可以让子类继承下去用
abstract class Animal {
    public Animal() {
        System.out.println("父类的构造方法执行了");
    }
    //abstract关键字:抽象的,可以修饰类,修饰方法
    public abstract void eat(); //抽象方法,此方法没有方法实现体
    public abstract void sleep();//抽象方法,此方法没有方法实现体
    //抽象类中既可以有抽象方法,也可以有非抽象方法
    public void show(){
        System.out.println("这是父类的一个非抽象方法");
    }
}
class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫爱吃鱼");
    }
    @Override
    public void sleep() {
        System.out.println("猫白天睡觉");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

在这里插入图片描述

案例演示2
抽象类的子类,要么重写父类中所有的抽象方法,要么自己也是一个抽象类

public class MyTest {
    public static void main(String[] args) {
    
    }
}
abstract class A {
    public abstract void a();
    public abstract void aa();
}
abstract class B extends A{//抽象类的子类,如果选择不重写抽象类的抽象方法,那么它必须也是一个抽象类
    public abstract void b();
}
class C extends B{//C类必须重写B类和A类的所有抽象方法
    @Override
    public void a() {

    }
    @Override
    public void aa() {

    }
    @Override
    public void b() {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

问1:一个类如果没有抽象方法,可不可以定义为抽象类 ? 如果可以,有什么意义?
答:可以,定义为抽象类,外界就不能直接创建该类的对象。
问2:abstract不能和哪些关键字共存?
答:

  1. private:如果用private修饰抽象方法,该方法不能被重写,矛盾。
  2. final:如果用final修饰抽象类,该类不能被继承,矛盾;如果用final修饰抽象方法,该方法不能被重写,矛盾。
  3. static:如果用static修饰抽象方法,方法变为静态方法,静态方法不存在重写。

(三)抽象类的成员特点

1.成员变量:既可以是变量,也可以是常量。
2.构造方法:有。

用于子类访问父类数据的初始化。

3.成员方法:既可以是抽象的,也可以是非抽象的。
  • 抽象方法: 强制要求子类重写。
  • 非抽象方法: 子类继承,提高代码复用性。

案例演示

public class MyTest {
    public static void main(String[] args) {

    }
}
abstract class A{
    //抽象类中的成员变量 即可定义变量也可以定义常量
    int num=100;
    public static final int num2=1000;
    //抽象类中有构造方法,用来让子类创建对象时初始化父类数据
    public A(int num) {
        this.num = num;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

二、接口

(一)接口概述

接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

接口被实现体现的是:”like a”的关系。 接口中定义的是该继承体系的扩展功能。

(二)接口特点

1.接口用关键字interface表示

格式:interface 接口名 {}

2.类实现接口用implements表示

格式:class 类名 implements 接口名 {}

3.接口不能实例化

那么,接口如何实例化呢?
按照多态的方式来实例化。

4.接口的子类

a:可以是抽象类。但是意义不大。
b:可以是具体类。要重写接口中的所有抽象方法。(推荐方案)
总而言之,除非实现接口的类是抽象类,否则该类要实现接口中的所有方法。

案例演示

public class MyTest {
    public static void main(String[] args) {
        Cat cat = new Cat();
        Animal an = cat;
        an.eat();
        an=new Dog();
        an.eat();
        // CalcInterface 是猫类的一个父接口,猫类也是父接口的一个子类
        CalcInterface c = cat;
        c.calc();
        //多态
        c= new Dog();
        c.calc();
        //接口不能直接实例化
    }
}
abstract class Animal {
    public abstract void eat();
}
interface CalcInterface {
    public abstract void calc();
}
class Cat extends Animal implements CalcInterface{//继承Animal类的同时实现CalcInterface接口
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
    @Override
    public void calc() {
        System.out.println("猫经过不断地努力学习,会做算术了");
    }
}
class Dog extends Animal implements CalcInterface{
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
    @Override
    public void calc() {
        System.out.println("狗经过不断地努力学习,会做算术了");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

在这里插入图片描述

(三)接口的成员特点

1.成员变量:只能是常量,并且是静态的。

默认(缺省)修饰符:public static final
建议:自己手动给出。

2.构造方法:接口没有构造方法。
3.成员方法:没有非抽象方法,只能是抽象方法。

默认(缺省)修饰符:public abstract
建议:自己手动给出。

案例演示

public class MyTest {
    public static void main(String[] args) {
        //接口中的成员变量全是静态的公共常量
    }
}
interface A{
    //接口中成员变量前面有默认的修饰符:public static final
    int a=100;
    public static final  int b=1000;
    //接口中成员方法前面存在默认修饰符:public abstract
    void test();
    public abstract void show();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

(四)接口的默认方法

在JDK1.8之后,接口中提供了用default修饰的默认方法,可以给出方法的具体实现,子类可以继承下去用
默认方法不是抽象方法,可以被继承,不强制重写,也可以重写

案例演示

public class MyTest{
    public static void main(String[] args) {
        C c= new C();
        c.test1();
        c.test22();
        c.show1();
        c.show11();
        A a=c;
        a.show1();
        a.test1();
        a.test2();
        B b=c;
        b.show11();
        b.test11();
        b.test22();
    }
}
interface A{
    void show1();
    //JDK1.8之后接口中可以定义默认方法,可以有方法的具体实现
    default void test1(){//前面缺省一个public,默认方法不是抽象方法
        System.out.println("test1");
    }
    default void test2() {
        System.out.println("test2");
    }
}
interface B {
    void show11();
    default void test11() {
        System.out.println("test11");
    }
    default void test22() {
        System.out.println("test22");
    }
}
class C implements A,B{
    @Override
    public void show1() {
        System.out.println("show1");
    }
    @Override
    public void show11() {
        System.out.println("show11");
    }
    //接口的默认方法不强制重写,可以直接继承用,当然也可以重写
    @Override
    public void test22() {
        System.out.println("Test22");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

在这里插入图片描述

三、区别与联系

(一)类与类、类与接口、接口与接口的关系

1.类与类:

继承关系。只能单继承,不能多继承,可以多层继承。

2.类与接口:

实现关系。可以单实现,也可以多实现,并且还可以在继承一个类的同时实现多个接口。

3.接口与接口:

继承关系。可以单继承,也可以多继承。

案例演示

public class MyTest {
    public static void main(String[] args) {
    
    }
}
class Fu{
    public void test(){
        
    };
}
interface A{
    void a();
}
interface B{
    void b();
}
class Zi extends Fu implements A,B{//可以继承一个类的同时实现多个接口
    @Override
    public void a() {
        
    }
    @Override
    public void b() {
        
    }
}
interface  C{
    void c();
}
interface D{
    void d();
    void dd();
}
interface E extends C,D{//接口和接口是继承关系,可以多继承
    void e();
    void ee();
}
class F implements E{//要实现E中和E继承的接口中的所有抽象方法
    @Override
    public void c() {
        
    }
    @Override
    public void d() {

    }
    @Override
    public void dd() {

    }
    @Override
    public void e() {

    }
    @Override
    public void ee() {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

(二)抽象类和接口的区别

1.成员区别
  • 抽象类
    成员变量:可以是变量,也可以是常量
    构造方法:有
    成员方法:可以是抽象方法,也可以是非抽象方法
  • 接口
    成员变量:只可以是常量
    构造方法:无
    成员方法:只可以是抽象方法
2.关系区别

具体见(一)

3.设计理念区别

抽象类被继承体现的是:”is a”的关系。 抽象类中定义的是该继承体系的共性功能。
接口被实现体现的是:”like a”的关系。 接口中定义的是该继承体系的扩展功能。

四、形式参数与返回值

(一)形式参数

1.类名作为形式参数

如果一个方法的形参要一个类 类型,那就传一个该类的对象

public class MyTest {
    public static void main(String[] args) {
        Student student = new Student();
        int num=2;
        //如果一个方法的形参要一个类 类型,就传一个该类的对象
        set(student,num);
        student.show(new Student(),100);
        System.out.println(student.num);
    }
    public static void set(Student student,int num){
        student.num=num;
    }
}
class Student{
    int num=10;
    public void show(Student student,int num){
        student.num=num;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这里插入图片描述

2.抽象类名作为形式参数

如果一个方法的形参要一个抽象类 类型,那就传一个该抽象类的子类对象

public class MyTest {
    public static void main(String[] args) {
        int num=1;
        Zi zi = new Zi();
        //如果一个方法的形参要一个抽象类 类型,那就传一个该抽象类的子类对象
        set(zi,num);
        System.out.println(zi.num); 
    }
    public static void set(Fu fu,int num){
        fu.num=num;
    }
}
abstract class Fu{
    int num=100;
    public abstract void show(int num);
}
class Zi extends Fu{
    int num=10;
    @Override
    public void show(int num) {
        this.num=num;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在这里插入图片描述

3.接口名作为形式参数

如果一个方法的形参要一个接口类型,那就传一个该接口的子类对象

public class MyTest {
    public static void main(String[] args) {
        int num=1;
        //如果一个方法的形参要一个接口类型,那就传一个该接口的子类对象
        B b = new B();
        set(b,num);
        System.out.println(b.num);
        System.out.println(b.NUM); 
        System.out.println(A.NUM); 
        System.out.println(B.NUM); 
    }
    public static void set(A a,int num){
        new B().num =num;
        a.show(num);
    }
}
interface A{
    int NUM=100;
    void show(int num);
}
class B implements A{
    int num =10;
    @Override
    public void show(int num) {
        this.num =num;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

在这里插入图片描述

(二)返回值

1.类名作为返回值类型
public class MyTest {
    public static void main(String[] args) {
        int num = 100;
        A a = new A();
        A a1 = a.getA(num);
        System.out.println(a.num);
        System.out.println(a1.num);
        System.out.println(a);
        System.out.println(a1);
        System.out.println(a == a1);
        A a2 =getAA(a,num);
        System.out.println(a.num);
        System.out.println(a2.num); 
        System.out.println(a);
        System.out.println(a2);
        System.out.println(a == a2);
    }
    public static A getAA(A a,int num) {
        A a1 = new A();
        a1.num = num;
        return a1;    //如果一个方法的返回值类型是一个类 类型,那就返回该类的一个对象
    }
}
class A {
    int num = 1;
    public A getA(int num) {
        A a = new A();
        a.num = num;
        return this;    //如果一个方法的返回值类型是一个类 类型,那就返回该类的一个对象
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

在这里插入图片描述

2.抽象类名作为返回值类型

如果一个方法的返回值类型要一个抽象类 类型,那就返回一个该抽象类的子类对象

public class MyTest {
    public static void main(String[] args) {
        Zi zi = new Zi();
        Fu fu = zi.getFu(zi, 109);
        System.out.println(zi.num); 
        System.out.println(fu.num); 
        System.out.println(zi==fu);
    }
}
abstract class Fu {
    int num = 10;
}
class Zi extends Fu {
    int num=100;
    public Fu getFu(Fu fu, int num) {
        fu.num = num;
        return this;//如果一个方法的返回值类型要一个抽象类 类型,那就返回一个该抽象类的子类对象
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这里插入图片描述

3.接口名作为返回值类型

如果一个方法的返回值类型要一个接口类型,那就返回该接口的一个子类对象

public class MyTest {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        int num=2;
        MyInterface myInterface = get(myClass, num);
        myClass.show(109);
        System.out.println(myClass.num); 
        System.out.println(myInterface.NUM);
    }
    public static MyInterface get(MyClass myClass,int num){
        myClass.num=num;
        return myClass;//如果一个方法的返回值类型要一个接口类型,那就返回该接口的一个子类对象
    }
}
interface MyInterface{
    int NUM=100;
    void show(int num);
}
class MyClass implements MyInterface{
    int num=1;
    @Override
    public void show(int num) {
        this.num=num;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在这里插入图片描述

五、链式编程

所谓的链式编程就是可以通过"点"语法,将需要执行的代码块连续的书写下去,使得代码简单易读,书写方便。

public class MyTest {
    public static void main(String[] args) {
        Student student = new Student();
        student.getStudent(student, 100).show(109);//链式编程
        //等价于:
        //Student student = new Student();
        //Student student1 = student.getStudent(student, 100);
        //student1.show(109);
        System.out.println(student.num); 
    }
}
class Student {
    int num = 10;
    public Student getStudent(Student stu, int num) {
        stu.num = num;
        return this;
    }
    public void show(int num) {
        this.num = num;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在这里插入图片描述

六、关键字package和import

(一)package

1.包(package)的概述

包,就是文件夹
为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。

2.包的作用
  • 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
  • 如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
  • 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。

Java 使用包(package)这种机制是为了防止命名冲突。

3.包的划分:
  • 按照功能
  • 按照模块
4.定义包的格式

package 包名;
多级包用.分开即可

5.定义包的注意事项

A:package语句必须是程序的第一条可执行的代码
B:package语句在一个java文件中只能有一个
C:如果没有package,默认表示无包名

案例演示

package com.study.util;

public class Student{

}
  • 1
  • 2
  • 3
  • 4
  • 5

那么它的保存路径应该是com/study/util/Student.java

(二)import

1.导包的概述

我们发现,每次使用不同包下的类的时候,都需要在类名的前面加包的全路径,比较麻烦。这个时候,java就提供了导包的功能

2.导包格式

import 包名.类名;导入该包的某个类
import 包名.*;导入该包的所有类(不建议)

一个.java文件中,书写顺序:package-import-class

七、修饰符

(一)权限修饰符

1.种类

private(私有的) 、默认、protected(受保护的)、public(公共的)

2.被修饰后的访问权限
本类同一个包下(子类和无关类)不同包下(子类)不同包下(无关类)
private
默认
protected
public

(二)关于修饰符

1.修饰符:

权限修饰符:private,默认的,protected,public
状态修饰符:static,final
抽象修饰符:abstract

2.修饰类的关键字:

权限修饰符:默认修饰符,public
状态修饰符:final
抽象修饰符:abstract

用的最多的就是:public

3.修饰成员变量的关键字:

权限修饰符:private,默认的,protected,public
状态修饰符:static,final

用的最多的就是:private

4.修饰构造方法的关键字:

权限修饰符:private,默认的,protected,public

用的最多的就是:public

5.修饰成员方法的关键字:

权限修饰符:private,默认的,protected,public
状态修饰符:static,final
抽象修饰符:abstract

用的最多的就是:public

6.除此以外的组合规则:

成员变量:public static final
成员方法:public static
        public abstract
        public final

八、内部类

(一)内部类概述

1.内部类定义: 把类定义在其他类的内部,这个类就被称为内部类。

举例:在类A中定义了一个类B,类B就是内部类。

2.内部类访问特点
  • 内部类可以直接访问外部类的成员,包括私有。
  • 外部类要访问内部类的成员,必须创建对象。
3.内部类分类

按照内部类位置分类

  • 成员位置:在成员位置定义的类,被称为成员内部类
  • 局部位置:在局部位置定义的类,被称为局部内部类

案例演示

public class B {

    //成员内部类
    class A {

    }

    public void show() {
        //局部内部类
        class C {

        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

(二)成员内部类

1.成员内部类的访问

如何在无关类中直接访问内部类的非私有成员。
先创建对象,格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;

案例演示

public class MyTest {
    public static void main(String[] args) {
        Outer outer = new Outer();
        System.out.println(outer.num);
        outer.outerShow();
        System.out.println("---------------");
        //在无关类创建成员内部类的语法
        //外部类名.内部类名 对象名 = 外部类对象.内部类对象;
        Outer.Inner inner=new Outer().new Inner();
        System.out.println(inner.b);
        //System.out.println(inner.c);报错
        inner.innerTest();
        inner.innerShow();
        System.out.println("---------------");
        outer.method();
    }
}
class Outer {//外部类
    int num = 10;
    private int a = 100;
    //定义成员内部类
    class Inner {
        int b = 109;
        private int c=1;
        public void innerShow() {
            System.out.println("内部类的show方法");
        }
        //内部类可以直接访问外部类的成员变量和成员方法,包括私有
        public void innerTest(){
            System.out.println(num);
            System.out.println(a);
            outerShow();
            outerTest();
        }
    }
    public void outerShow() {
        System.out.println("外部类的show方法");
    }
    private void outerTest() {
        System.out.println("外部类的test方法");
    }
    //外部类,想要访问内部类的成员,得创建内部类的对象
    public void method(){
        //创建内部类的对象
        Inner inner = new Inner();
        System.out.println(inner.b);
        System.out.println(inner.c);//外部类访问内部类私有成员变量
        inner.innerShow();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

在这里插入图片描述

2.成员内部类的常见修饰符
  • private 为了保证数据的安全性
  • static 为了方便访问数据

注意事项:

  • 静态内部类访问的外部类数据必须也是静态的。
  • 静态内部类的成员方法可以是静态的也可以是非静态的

案例演示1
被private修饰的成员内部类

public class MyTest {
    public static void main(String[] args) {
        //创建内部类的对象
        //内部类被private修饰了,外界就不能直接创建内部类对象了
        //Wai.Nei nei = new Wai().new Nei();不适用
        //那外界该怎么访问私有的成员内部类呢?
        //可以通过外部类的方法访问
        Wai wai = new Wai();
        wai.waiShow();
    }
}
class Wai{
    //private 可以修饰内部类
    private class Nei{
        int num=10;
        public void neiShow(){
            System.out.println("内部类的show方法");
        }
    }
    public void waiShow(){
        Nei nei = new Nei();
        System.out.println(nei.num);
        nei.neiShow();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在这里插入图片描述
案例演示2
被static修饰的成员内部类
成员内部类被静态修饰后的访问方式是:
格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();

public class MyTest {
    public static void main(String[] args) {
        //内部类被静态修饰后,创建内部类的语法要改变
        Wai.Nei nei = new Wai.Nei();
    }
}
class Wai {
    static int num = 10;
    private int a = 100;

    static class Nei {
        public void neiShow() {
            //静态内部类要访问外部类的成员只能访问静态的
            System.out.println(num);
            waiShow();
            //System.out.println(a);报错
        }
    }
    public static void waiShow() {
        System.out.println("外部类的show方法");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

思考题
要求:使用已知的变量,在控制台输出30,20,10。

class Outer {
	public int num = 10;
	class Inner {
		public int num = 20;
		public void show() {
			int num = 30;
			System.out.println(填空); //30                   
			System.out.println(填空); //20               
			System.out.println(填空); //10        
		}
	}
}
class InnerClassTest {
	public static void main(String[] args) {
		Outer.Inner oi = new Outer().new Inner();
		oi.show();
	}	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

答案
num
this.num 或 Inner.this.num
new Outer().num 或 Outer.this.num

(三)局部内部类

与成员内部类不同的是,局部内部类,外界不能直接创建其对象,只能通过外部类访问

public class MyTest {
    public static void main(String[] args) {
        //与成员内部类不同的是,局部内部类,外界不能直接创建其对象,只能通过外部类访问
        Outer outer = new Outer();
        outer.waiShow();
    }
}
class Outer {
    int num=10;
    private int a=100;
    public void waiShow(){
        //定义局部内部类
        //内部类可以直接访问外部类的成员变量和成员方法,包括私有的
        class Inner{
            int c=1;
            public void neiShow(){
                System.out.println(c);
                System.out.println(num);
                System.out.println(a);
            }
        }
        //外部类访问内部类要创建对象
        Inner inner = new Inner();
        System.out.println(inner.c);
        inner.neiShow();
        //除了局部内部类所在方法的外界无法直接访问该内部类
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

在这里插入图片描述
注意
局部内部类访问的局部变量必须用final修饰
为什么呢?
因为局部变量的生命周期与局部内部类的对象的生命周期的不一致,局部变量会随着方法的调用完毕而消失,这个时候,局部内部类的对象并没有立马从堆内存中消失(只有当没有人再引用该对象时,它才会消失,它不会随着所在方法运行结束而消失)。为了让数据还能继续被局部内部类对象使用,就用final修饰,这样,通过final将局部变量"复制"一份,复制品直接作为局部内部中的数据成员。因此,当运行栈中的真正的局部变量消失时,局部内部类对象仍可以访问局部变量。给人的感觉:好像是局部变量的"生命期"延长了。

案例演示

public class MyTest{
    public static void main(String[] args) {
        Outer out = new Outer() ;
        out.show() ;
    }
}
class Outer {
    // 成员变量
    private int a = 40 ;
    // 成员方法
    public void show() {
        // 定义一个局部变量
        int b = 45 ;//JDK1.7之前要加final不然报错,但是在JDK1.8默认加上了,不会报错
        // 局部内部类
        class Inner {
            public void method(){
                System.out.println(a) ;
                System.out.println(b) ;
                //b=50;//报错,final修饰的变量不可改变
            }
        }
        // 创建对象
        Inner i = new Inner() ;
        // 调用方法
        i.method() ;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

在这里插入图片描述

(四)匿名内部类

1.匿名内部类的格式和理解

(1)匿名内部类:就是局部内部类的简化写法。
(2)前提:存在一个类或者接口;这里的类可以是具体类也可以是抽象类。
(3)格式:

new 类名或者接口名(){
		重写方法;
} ;
123
  • 1
  • 2
  • 3
  • 4

(4)本质是什么呢?
是一个继承了该类或者实现了该接口的子类匿名对象。

案例演示1

public class MyTest{
    public static void main(String[] args) {
        //匿名内部类:是局部内部类的简写
        //不需要再多定义一个类继承A类
        new A(){

        };
        //还可以通过匿名内部类调用方法
        new A(){

        }.test();
    }
}
class A{
    public void test(){
        System.out.println("test");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述
案例演示2

public class MyTest {
    public static void main(String[] args) {
        //匿名内部类,本质上是一个对象,是谁的对象,是实现了该接口或继承了该抽象类的子类对象
        new AA(){
            @Override
            public void show() {
                System.out.println("重写了show方法");
            }
        }.show();
    }
}

abstract class AA{
    public abstract void show();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述
案例演示3

public class MyTest{
    public static void main(String[] args) {
        //一个匿名内部类只能调用一个方法
        new MyInterface(){
            @Override
            public void show() {
                System.out.println("重写了接口中的show方法");
            }

            @Override
            public void test() {
                System.out.println("重写了接口中test方法");
            }
        }.show();
        //一个匿名内部类只能调用一个方法
        new MyInterface() {
            @Override
            public void show() {
                System.out.println("重写了接口中的show方法");
            }

            @Override
            public void test() {
                System.out.println("重写了接口中test方法");
            }
        }.test();
        //那我想同时调用两个方法怎么办?
        //给匿名内部类起个名字
        MyInterface my= new MyInterface() {
            @Override
            public void show() {
                System.out.println("重写了接口中的show方法");
            }

            @Override
            public void test() {
                System.out.println("重写了接口中test方法");
            }
        };
        //同一个对象,去调用两个方法
        my.show();
        my.test();
    }
}
interface MyInterface{
    void show();
    void test();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
2.匿名内部类在开发中的应用

方法的形式参数或返回值是引用类型的情况,通常需要一个子类对象,而匿名内部类就是一个子类匿名对象,所以,可以使用匿名内部类改进以前的做法。
案例演示1
匿名内部类作为形式参数

public class MyTest {
    public static void main(String[] args) {
        //匿名内部类可以作为参数传递
        //调用set方法,参数为匿名内部类
        set(new MyInterface() {
            @Override
            public void show() {
                System.out.println("重写了show方法");
            }

            @Override
            public void test() {
                System.out.println("重写了test方法");
            }
        });
        //方法二
        MyInterface my= new MyInterface() {
            @Override
            public void show() {
                System.out.println("重写了show方法2222");
            }

            @Override
            public void test() {
                System.out.println("重写了test方法2222");
            }
        };
        set(my);
    }
    public static void set(MyInterface myInterface){
        myInterface.show();
        myInterface.test();
    }
}
interface MyInterface{
    void show();
    void test();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

在这里插入图片描述
案例演示2
匿名内部类作为返回值

public class MyTest {
    public static void main(String[] args) {

        A a = getA();
        a.show();
    }
    public static A getA(){
        return new A() {
            @Override
            public void show() {
                System.out.println("重写了show方法");
            }
        };
    }
}
abstract class A {
    public abstract void show();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述


思考题
按照要求,补齐代码,要求在控制台输出”HelloWorld”

class MyTest {
	public static void main(String[] args) {
		 Outer.method().show();
	 }
}
interface Inter { 
	void show(); 
}
class Outer { //补齐代码 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Outer.method()是用类去调的方法,证明method方法必须是静态的,而后边还紧接着链式编程跟了一个show方法,证明Outer.method方法返回的是一个对象,而show方法在Inter接口中,因此Outer.method返回的应该是这个接口的子类对象,才能调用show方法。

public class MyTest {
    public static void main(String[] args) {
        //链式编程
        Outer.method().show();
    }
}
//接口
interface Inter {
    void show();
}
class Outer {
    public static Inter method(){
        return new Inter() {
            @Override
            public void show() {
                System.out.println("helloworld");
            }
        };
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
3.匿名内部类中的this关键字
interface Inter {
	public static final int a = 23 ;
}
public class MyTest {
	public static void main(String[] args) {
		new Inter() {
			public void show() {
			// 这个this表示的是匿名内部类的这个对象
				System.out.println(this.a);
			}
		}.show();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

this表示的就是这个匿名内部类的对象,而它又是父接口的实现类,因此它可以使用接口中的成员变量。

4.类中定义接口
public class MyTest {
    public static void main(String[] args) {
        //要想重写show方法,有几种方式?
        //间接方式(通过外部类创建内部接口的匿名内部类对象来重写)
        Outer outer = new Outer();
        outer.waiShow();
        //直接方式(通过直接创建接口的匿名内部类对象重写)
        new Outer.Inner() {
            @Override
            public void show() {
                System.out.println("重写了接口中的show方法2");
            }
        }.show();
    }
}
class Outer{
    //成员内部接口(而局部不能定义接口)
    interface Inner{
        void show();
    }
    public void waiShow(){
        //匿名内部类
        new Inner(){
            @Override
            public void show() {
                System.out.println("重写了接口中的show方法");
            }
        }.show();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

在这里插入图片描述

(五)四大函数式接口

理解Functional Interface(函数式接口)是学习Java8 Lambda表达式的关键所在。
函数式接口的定义:任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
在这里插入图片描述

1.函数型接口 Function

在这里插入图片描述
案例演示

import java.util.function.Function;

public class MyTest{
    public static void main(String[] args) {
        //Function函数型接口,有一个输入参数,有一个输出
        Function<String,String> function=new Function<String,String>() {
            @Override
            public String apply(String str) {
                return str;
            }
        };
        System.out.println(function.apply("abcd"));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述

2.断定型接口 Predicate

在这里插入图片描述
案例演示

import java.util.function.Predicate;

public class MyTest{
    public static void main(String[] args) {
        Predicate<String> predicate = new Predicate<String>() {
            @Override
            public boolean test(String str) {
                return str.isEmpty();
            }
        };
        System.out.println(predicate.test(""));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述

3.消费型接口 Consumer

在这里插入图片描述
案例演示

import java.util.function.Consumer;

public class MyTest{
    public static void main(String[] args) {
    //有参数,无返回值
        Consumer<String> consumer = new Consumer<String>(){
            @Override
            public void accept(String str) {
                System.out.println(str);
            }
        };
        consumer.accept("abcd");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述

4.供给型接口 Supplier

在这里插入图片描述
案例演示

import java.util.function.Supplier;

public class MyTest{
    public static void main(String[] args) {
    //没有参数,有返回值
        Supplier<Integer> supplier = new Supplier<Integer>(){
            @Override
            public Integer get() {
                return 1024;
            }
        };
        System.out.println(supplier.get());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述

(六)Lambda表达式

对于函数式接口,我们可以通过Lambda表达式来创建该接口的对象。
λ是希腊字母表中排序第十一位的字母,英语名称为Lambda,Lambda表达式其实质属于函数式编程的概念。

(params)-> expression [ 表达式 ]`
`(params)-> statement [ 语句 ]`
`(params)-> { statements }
  • 1
  • 2
  • 3

为什么要用Lambda表达式?

  • 避免匿名内部类定义过多
  • 去掉了一堆没有意义的代码,只留下核心的逻辑,看起来更简洁

案例演示1
外部类、成员内部类、局部内部类、匿名内部类、Lambda表达式

interface Like{//函数式接口:只有一个抽象方法
    void like();
}
//1.外部类
class Like1 implements Like{
    @Override
    public void like() {
        System.out.println("I like lambda1");
    }
}

public class MyTest {
    //2.成员内部类
    private static class Like2 implements Like{
        @Override
        public void like() {
            System.out.println("I like lambda2");
        }
    }

    public static void main(String[] args) {
        Like l = new Like1();
        l.like();
        l = new Like2();
        l.like();
        //3.局部内部类
        class Like3 implements Like{
            @Override
            public void like() {
                System.out.println("I like lambda3");
            }
        }
        l=new Like3();
        l.like();

        //4.匿名内部类
        l=new Like() {
            @Override
            public void like() {
                System.out.println("I like lambda4");
            }
        };
        l.like();

        //5.lambda表达式
        l=()->{
            System.out.println("I like lambda5");
        };
        l.like();
        //也可以写成:
        //l=()-> System.out.println("I like lambda5");
        //l.like();

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

在这里插入图片描述
案例演示2
抽象方法带参数的Lambda表达式;Lambda表达式的一步步简化

interface Like{//函数式接口:只有一个抽象方法
    void like(int a);
}
//1.外部类
class Like1 implements Like{
    @Override
    public void like(int a) {
        System.out.println("I like lambda"+a);
    }
}

public class MyTest {
    //2.成员内部类
    private static class Like2 implements Like{
        @Override
        public void like(int a) {
            System.out.println("I like lambda"+a);
        }
    }

    public static void main(String[] args) {
        Like l = new Like1();
        l.like(1);
        l = new Like2();
        l.like(2);
        //3.局部内部类
        class Like3 implements Like{
            @Override
            public void like(int a) {
                System.out.println("I like lambda"+a);
            }
        }
        l=new Like3();
        l.like(3);

        //4.匿名内部类
        l=new Like() {
            @Override
            public void like(int a) {
                System.out.println("I like lambda"+a);
            }
        };
        l.like(4);

        //5.lambda表达式
        l=(int a)->{
            System.out.println("I like lambda"+a);
        };
        l.like(5);

        //6.简化lambda表达式:去掉参数类型
        l=(a)-> {
            System.out.println("I like lambda"+a);
        };
        l.like(6);

        //7.简化lambda表达式:去掉括号
        l=a-> {
            System.out.println("I like lambda"+a);
        };
        l.like(7);

        //8.简化lambda表达式:去掉花括号(前提:抽象方法的逻辑只有一行)
        l=a->System.out.println("I like lambda"+a);
        l.like(8);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

在这里插入图片描述

案例演示3
抽象方法有多个参数的Lambda表达式

interface Like{//函数式接口:只有一个抽象方法
    void like(int a,int b,int c);
}
//1.外部类
class Like1 implements Like{
    @Override
    public void like(int a,int b,int c) {
        System.out.println("I like lambda"+a+b+c);
    }
}

public class MyTest {
    //2.成员内部类
    private static class Like2 implements Like{
        @Override
        public void like(int a,int b,int c) {
            System.out.println("I like lambda"+a+b+c);
        }
    }

    public static void main(String[] args) {
        Like l = new Like1();
        l.like(1,1,1);
        l = new Like2();
        l.like(2,2,2);
        //3.局部内部类
        class Like3 implements Like{
            @Override
            public void like(int a,int b,int c) {
                System.out.println("I like lambda"+a+b+c);
            }
        }
        l=new Like3();
        l.like(3,3,3);

        //4.匿名内部类
        l=new Like() {
            @Override
            public void like(int a,int b,int c) {
                System.out.println("I like lambda"+a+b+c);
            }
        };
        l.like(4,4,4);

        //5.lambda表达式
        l=(int a,int b,int c)->{
            System.out.println("I like lambda"+a+b+c);
        };
        l.like(5,5,5);

        //6.简化lambda表达式:去掉参数类型
        l=(a,b,c)-> {
            System.out.println("I like lambda"+a+b+c);
        };
        l.like(6,6,6);

        //7.简化lambda表达式:去掉花括号(前提:抽象方法的逻辑只有一行)
        l=(a,b,c)->System.out.println("I like lambda"+a+b+c);
        l.like(7,7,7);

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

在这里插入图片描述

总结:

  • 要使用Lambda表达式,前提是接口必须是函数式接口
  • 抽象方法的逻辑只有一行的情况下,Lambda表达式才能去掉花括号
  • 多个参数的情况下,也能去掉参数类型,要去掉就都去掉,但是括号不能去掉
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/180492
推荐阅读
相关标签
  

闽ICP备14008679号