Java核心技术 卷I 第六章

本文详细介绍了Java中的接口,包括Comparable接口、Arrays.sort方法、接口特性及默认方法冲突解决。接着讨论了对象克隆,讲解了clone方法、深浅拷贝以及Cloneable接口。然后,深入探讨了lambda表达式,包括函数式接口、方法引用和构造器引用。此外,还阐述了内部类的不同类型及其特点,以及代理的概念和创建代理对象的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java核心技术 卷I 第六章

第六章 接口、lambda表达式与内部类



思维导图

请添加图片描述
请添加图片描述
请添加图片描述

6.1 接口

Comparable接口,带泛型

public interface Comparable<T> {
    public int compareTo(T o);
}

关于Arrays.sort(Object[] a)方法

public static void sort(Object[] a) {
    if (LegacyMergeSort.userRequested)
        legacyMergeSort(a);
    else
        ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}

对一个数组排序,要求数组元素必须实现Comparable接口的

接口特性

1、接口不是类,可以用接口声明对象(多态)
2、可用instanceof检查对象是否实现了特定接口
3、接口可extends接口
4、接口不能有实例域
5、接口可以有静态方法带方法体(java8允许)
6、接口可有常量 public static final
7、接口中的方法自动带修饰符public,域自动带public static final(常量)
8、接口可什么也不声明,如Cloneable

public interface Cloneable {
	}

9、接口的default方法可选择实现(带方法体)

接口标准格式:

public interface InterfaceName{
	//域-常量
	public static int finalVar = 1;
	//方法-静态(有方法体)
	public static void staticMethod(){
		System.out.println("我是接口的静态方法");
	}
	//方法-实例(要实现的方法,无方法体)
	public void instanceMethod();
	//方法-默认(可选择实现的方法,有方法体)
	public default void defaultMethod(){
		System.out.println("我是接口的默认方法");
		//默认方法可调用实例方法
		instanceMethod();
	}
	//内部类-静态
	public static class StaticInnerClass{
		
	}
}

解决默认方法冲突

什么是默认方法冲突?

	SubClass extends SuperClass implements Interface,SuperInterface{
	}

子类继承超类又实现接口,接口中的默认方法会和超类的同名方法冲突
编译规则:
1)超类优先
超类中的public方法最大,protected和default方法均和接口中的默认方法冲突
解决超类和接口方法的冲突:选择一个实现即可
注意:
如果子类不在父类同包下,子类继承不了父类的同包方法
选择重写实现的方法修饰符都只能是public
父类中受保护的方法

@Override
    public void protectMethod() {
        super.protectMethod();//父类实现
        Interface.super.protectMethod();//接口实现
    }

父类中的同包方法

@Override
    public void defaultMethod() {
        super.defaultMethod();//父类实现
        Interface.super.defaultMethod();//接口实现
    }

2)接口冲突
1.类同时实现子接口和父接口,子接口继承父接口,两同名默认方法不会发生冲突
默认是得到了子接口的默认方法,且不允许改用父接口的默认
2.类同时实现接口1和接口2,两同名方法(其中一个为默认)会发生冲突
解决:选择一个实现
3.类同时实现接口1和接口2,两同名非默认方法,不会发生冲突

@Override
    public void mMethod() {
        Interface1.super.mMethod();//接口1实现
        Interface2.super.mMethod();//接口2实现
    }

Comparator接口

Arrays.sort方法的第二个版本,参数(数组,比较器)
比较器是实现了Comparator的实例

Comparator接口

public interface Comparator<T> {
	    int compare(T o1, T o2);
	}

//LengthComparator实现了Comparator<T>接口,指定了T为String
//实现了compare方法
//用字符串长度作为比较依据
String[] friends = {"Peter","Paul","Tom"};
Arrays.sort(friends,new LengthComparator());

6.2 对象克隆

clone 方法是 Object 的一个 protected 方法
这说明你的代码不能直接调用这个方法
如a.clone();只能在a的内部调用

1)浅拷贝(默认)
如果原对象和浅克隆对象共享的子对象是不可变的,那么这种共享就是安全的
例子:域全是基本数据类型和不可变数据类型String

2)深拷贝
Cloneable接口没有指定clone方法,这个方法是从Object类继承的,这个接口什么都没有,只是作为一个标记,指示类的设计者。
对象对克隆很‘偏执’,如果一个对象请求克隆,但没有实现这个接口,就会产生一个受查异常

标记接口

没有方法,唯一用途就是允许在类型查询中使用instanceof

if(obj instanceof Cloneable){
	...
}

实现接口重写clone

所有的克隆需求均需要实现Cloneable接口,将clone重定义为public,再调用super.clone();例:

class Employee implements Cloneable{
	@Override
	//重写Object的克隆方法,将其声明为public,并修改返回值类型
	public Employee clone() throws CloneNotSupportedException{
		return (Employee)super.clone();
	}
}

如果在一个对象上调用clone,但这个对象的类并没有实现Cloneable接口,Object类的clone方法就会抛出一个CloneNotSupportedException,尽管是类和实例实现了Cloneable接口,但编译器不了解,仍然需要声明抛出异常

深拷贝案例
深拷贝案例

数组克隆

所有数组类型都有一个public的clone方法,可以用这个方法建立一个新数组。

int[] a = {1,2,3,4,5,6,7,8};
int[] cloned = a.clone();

6.3 lambda

lambda表达式即为一个函数式接口

语法格式一(单句表达式无 return):

(参数列表)->返回值表达式

语法格式二(多句显示return):

(参数列表)->{
	语句1;
	语句2;
	return 返回值;
	}
//单句表达式的lambda
Comparator<String> stringComparator = (String first, String second)
                -> first.length() - second.length();

Consumer<String> stringConsumer = (String a) -> System.out.println(a);

6.3.3 函数式接口

java中已经有很多封装代码块的接口,如ActionListener或Comparator。
lambda表达式与这些接口是兼容的。

对于只有一个抽象方法的接口,需要这种接口的对象就可以提供一个lambda表达式

不能把lambda表达式赋值给Object,Object并不是函数式接口

在这里插入图片描述

6.3.4 方法引用

object::instanceMethod
Class::staticMethod
Class::instanceMethod
this::instanceMethod
super::instanceMethod

6.3.5 构造器引用

Person::new
int[]::new

6.3.6 lambda表达式内的变量

lambda的内部变量可捕获外围作用域的值,但必须是不会改变的量(final)

int b = 1;
(String a) -> System.out.println(b)

规则:
1)lambda表达式中捕获的变量必须实际上是最终变量(final)
2)lambda表达式声明的局部变量不能和之前的同名
3)lambda 表达式中使用this关键字,this指代创建这个表达式的方法的所属对象

6.4 内部类

内部类可访问定义其类所在作用域中的数据(包括私有)
内部类在外部同包中无法被访问(对同包隐藏)

6.4.2 内部类特殊语法规则

1.在内部类中调用外围类引用:OuterClass.this.域
2.反之,外部类new内部类对象:this.new 内部类名();
3.在外围类的作用之外,可以这样引用内部类:OuterClass.InnerClass
4.内部类不能有static方法
5.内部类静态域必须是final

6.4.3 内部类编译的class文件

Tes3t类中的内部类A和内部类B 还有Test3类本身
在这里插入图片描述

6.4.4 局部内部类

void m(){
    class AAAA{

        int i;
        static final int b = 0;
        public void m2(){
            System.out.println("我是局部内部类");
        }

    }
    new AAAA().m2();
}

局部内部类生成.class文件规则: 主类$编号局部内部类名.class
在这里插入图片描述
规则:
局部内部类不能用public 和private声明,作用域是声明局部类的块中
局部内部类的绝对优势:对外部完全隐藏,即使是最外部的本类中也无法访问。

6.4.6 匿名内部类

语法:
接口/超类 名字 = new 接口/超类(参数列表)
{
接口要实现的代码;
}

public static void main(String[] args) {
    TestInterface testInterface = new TestInterface() {
        @Override
        public void m3() {
            System.out.println("我被匿名内部类实现了");
        }
    };

    testInterface.m3();
}

6.4.7 静态内部类

public class A{
	public static class 类名{

	}
}

静态方法中只能用静态内部类,否则抛异常
与常规内部类不同,静态内部类可以有静态域和方法
接口中也可以有内部类,自动修饰为public static class 类名{} 为静态内部类

6.5 代理

有接口class对象,如何构造这个实现类呢?
正常是使用反射的class.newInstance方法来找出这个类的构造器,但是不能实例化一个接口

代理:创建一个全新的类,该类proxy可代替我们想要扩充功能进行表演的类A,就是用proxy类代替A,扩充功能或者直接替代

1)代理类需要和被代理类实现同一个接口

在这里插入图片描述
提供调用处理器(invocation handler)
调用处理器是实现了InvocationHandler接口的类对象,实现的方法:

//proxy 是返回值,method是要增强的方法,args是方法的参数列表
Object invok(Object proxy,Method method,Object[] args)

6.5.2 创建代理对象

需要使用Proxy类的newProxyInstance方法,三个参数
1)一个类加载器,null为默认的类加载器
2)一个Class对象数组,每个元素都是需要实现的接口
3)一个调用处理器

定义一个处理器:

public class TraceHandler implements InvocationHandler {
    
    private Object target;

    public TraceHandler(Object target) {
        this.target = target;
    }
	//等同于target.method(args);
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(target,args);
    }
}

利用这个处理器来构建代理对象
不想构建处理器类,可以使用匿名内部类,第三个参数就是处理器,用匿名内部类+lambda表达式方式实现

public class DynamicTest {
    public static void main(String[] args) {
        Customer customer = new Customer();
        //获得代理对象,用接口多态声明,对Proxy.newProxyInstance(...)强转
        OrderInterface deliver = (OrderInterface)Proxy.newProxyInstance(
       			//参数1,一个类加载器
                Customer.class.getClassLoader(),
                //参数2,接口class对象
                new Class[]{OrderInterface.class},
                //参数3,处理器----对某些方法进行增强
                (Object proxy, Method method, Object[] arg) -> {
                	//对部分原方法进行增强,也可以不增强
                    if(method.getName().equals("order")) {
                        System.out.println("增强下单方法");
                        return method.invoke(customer, arg);
                    }
                    else{
                        return method.invoke(customer, arg);
                    }
                }
        );
        deliver.m1();//未增强方法
        deliver.order("麻婆豆腐");//增强了的方法
        System.out.println(deliver.getClass());//class com.sun.proxy.$Proxy0
    }
}

那么这个生成的代理类对象究竟是什么class?
是com.sun.proxy包下的以$proxy开头的类名
对同一个类进行代理多次,都是$proxy0名字的类

代理第二个类(接口不变),则类名为:仍然是$proxy0

代理第二个不同接口的类,则类名为:$proxy1

依次按照接口不同编号递增

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值