Java基础 自学讲义 3.接口 lambda表达式和内部类

目录
一.接口
1.接口基本定义
2.接口的特性
二.接口实例
1.接口与回调
2.Comparator接口
3.对象克隆
三.lambda表达式
1.基础语法
2.

一.接口

1.接口基本定义

所谓接口就是某个类的一些方法的声明集合, 用于整理这个类的一些需要用的方法, 我感觉类似于C++中的.h头文件, 在接口中只做一些声明而不做具体的定义, 具体的定义放在类的实现中;
比如Comparable接口, 这是一个泛型接口, 例如:

	public interface Comparable<T>{
		int compareTo(T other);// parameter has type T
	}

这就意味着, 所有支持Comparable这个接口的类, 一定要包含有compareTo方法, 输入是泛型(有的接口会规定具体的类型), 返回值是int型, 而且所有的接口中的具体方法在实现的时候都必须是public, 而且也不能省略, 否则会因为privacy问题被编译器报错;
下面上一个我自己写的用前面的RichPeople类改写的接口实现对RichPeople类对象进行按Money的排序:

import java.util.*;

public class test6 {
	public static void main(String[] args) {
		People[] staff = new People[3];
		staff[0] = new RichPeople("Amos",20,100,1000);
		staff[1] = new RichPeople("Abis",20,100,10);
		staff[2] = new RichPeople("Adam",20,100,10);
		for(Object i : staff){System.out.println(i);}
		System.out.println();
		Arrays.sort(staff);
		for(Object i : staff){System.out.println(i);}
	}
}

abstract class Person{
...
}

class People extends Person{
...
}

class RichPeople extends People implements Comparable<RichPeople>{
	...
	public int compareTo(RichPeople u){
		return Integer.compare(this.getMoney(), u.getMoney());
	}
}

我省略掉了一些不太重要的前面出现过的代码, 重点关注这一部分:

class RichPeople extends People implements Comparable<RichPeople>{
	...
	public int compareTo(RichPeople u){
		return Integer.compare(this.getMoney(), u.getMoney());
	}

我实现了一个支持Comparable接口的RichPeople类, 同时我重写了compareTo方法, 如果输入对象的Money大于this的Money则会返回一个负数, 如果输入对象的Money小于this的Money则会返回一个整数, 如果相等则会返回0;
所以上面的函数的输出结果如下:

RichPeople[Name:Amos,Age20,Money:1100]
RichPeople[Name:Adis,Age20,Money:110]
RichPeople[Name:Abam,Age20,Money:110]

RichPeople[Name:Adis,Age20,Money:110]
RichPeople[Name:Abam,Age20,Money:110]
RichPeople[Name:Amos,Age20,Money:1100]

2.接口的 特性

接口的定义方式大概长这样:

public interface zilean{
	int time=10;
	void timeprint();
}

在接口内部可以定义域, 所有的域会被自动修饰成public static final;
(如果把类和接口放在同一个文件中, 要把接口或者类的public修饰符去掉一个, 即同一个文件中只能存在一个public的修饰符修饰的类或者枚举或者接口等等)
接口也是可以被继承的,像下面这样写:

interface zileanson extends zilean {
	int time=11;
	void timeprint();
}

可以用接口名来声明某一个变量, 但是不能直接new一个接口类型, 但是可以把这个变量指向一个对象, 这个对象必须要是支持这个接口的;
可以用instanceof来判断某个变量是否是属于某个接口的对象;
我自己的测试代码如下:

import java.util.*;
import java.time.*;

public class test6 {
	public static void main(String[] args) {
		zilean a = new people("zilean", 21);
		people x = (people)a;
		x.timeprinto();
		x.tt();
		zilean.ti();
	}
}

class people implements zilean, zileanson{
	private String name = "";
	private int age = 0;
	public people(String name, int age){
		this.name = name;
		this.age = age;
	}
	public people(){}
	public String getName(){
		return this.name;
	}
	public void timeprint(){
		System.out.println(zilean.time);
	}
	public void timeprinto(){
		System.out.println(this.name+" changed the time to "+LocalDate.now()+"\n");
	}
}

interface zilean{
	int time=10;
	void timeprint();
	public static void ti(){
		System.out.println("ti");
	}
	default void tt(){System.out.println("tttttt");}
}

interface zileanson extends zilean {
	int time=11;
	void timeprint();
}

对于java中接口的一些功能, 其实也可以放在类的超类(抽象类)中写, 但是因为java不支持多继承, 所以不能继承多个抽象类, 但是一个类可以支持多个接口, 所以接口基本可以提供多继承的很多好处, 在一些支持多继承的语言中, 比如C++就可以使用多继承来实现接口的一些特性;
在接口中可以写静态方法,调用的时候可以直接zilean.ti();但是要记得用public static修饰;
在接口中可以为一个写默认方法, 用default修饰就可以, 可以写一个默认的方法实现, 虽然规定要在类的内部重写接口中的每个方法, 但是如果写了default的话还是会比较方便;
关于默认方法可能会带来的一些问题:
如果先在一个接口中将一个方法定义为默认方法, 然后又在超类或另一个接口中定义了同样的方法,那么就会产生冲突,;
Java对此的解决方法是: 超类方法优先;
但是, 当两个接口中提供了同名方法而且都写了默认方法时, 那么会产生冲突, 编译器会报错;
即使两个接口中提供了同名方法但是只有一个写了默认方法, 编译器也会报错, 而不会自动选择默认方法;
可以这样来选择一个方法的实现方法:

class student implements named,person{
	public getName(){
		return named.getName();
	}
}

正因为java这样的冲突处理方式是超类继承优先的, 所以不要试图用默认方法在接口中重写在Object类中的一些方法, 比如toString方法和equals方法, 因为这样肯定是会被Object的方法覆盖掉的;

二.接口示例

1. 接口与回调(callback)

import java.awt.event.*;
import java.util.*;
import javax.swing.Timer;
import javax.swing.*;
import java.awt.*;

class test7 {
	public static void main(String[] args) {
		ActionListener ac = new act();
		javax.swing.Timer timeac = new Timer(3000, ac);
		timeac.start();
		JOptionPane.showMessageDialog(null, "quit program?");
		System.exit(0);
	}
}

class act implements ActionListener{
	public void actionPerformed(ActionEvent event){
		System.out.println("now time is : "+new Date());
		Toolkit.getDefaultToolkit().beep();
	}
}

回调指的是在某个特定事件发生后会采取特定的某些操作, 比如上面的程序就是在计时器每3000ms会打印出一行当前时间, 并且调用系统发出通告声音beep();
在这里不需要对代码的具体内容和实现太关注, 只需要关注这种回调的设计模式就可以了;
上面的代码中定义了一个支持ActionListener的act类, 在其中重写了actionPerformed方法, 每当发生一个event事件都会打印出一条通告信息并且执行beep();

2.Comparator接口

我们可以自己实现一个支持Comparator比较器接口的类, 比如下面的程序可以把一个字符串数组按字符串的长度排序:

import java.util.*;

public class test8{
	public static void main(String[] args) {
		String[] s = {"John", "Samira", "Mustafa", "Adam", "Recluse"};
		for(String i : s) System.out.println(i);
		Arrays.sort(s, new lengthcompare());
		System.out.println();
		for(String i : s) System.out.println(i);
		
	}
}

class lengthcompare implements Comparator<String>{
	public int compare(String a, String b){
		return a.length()-b.length();
	}
}

我们在lengthcompare类中对Comparator接口进行继承, 自己实现了compare方法, 返回值是a.length()-b.length();以实现按字符串长度比较进行排序的函数;
在调用比较器的时候可以直接传入一个支持Comparator接口的类作为比较器;

3.对象克隆

如果我们想把一个对象拷贝的话, 要是直接使用=, 其结果会直接把新声明的变量指向原先的对象, 比如:

People a = new People("Amos", 21);
People b = a;

这样b其实只是对a的一个引用, 所有对b的值进行改变的操作都会改变a内部的值, 如果希望能独立的拷贝一个与a完全相同的对象, 但是因为clone方法是Object方法的一个方法, 虽然是protected的方法, 但是并不能直接调用clone方法, 要在类内部重新把clone定义为public修饰的方法才能直接调用, 同时, 还要注意捕获异常error: unreported exception CloneNotSupportedException; must be caught or declared to be thrown比如可以使用try catch进行异常捕获:

class People implements Cloneable {
	
	public static void main(String[] args){
		People a = new People("Amos", 21);
		System.out.println(a.getAge());
		People b = (People)a.clone();
		b.AddAge(10);
		System.out.println(a.getAge());
		System.out.println(b.getAge());
	}
	
...

	public People clone(){
		try {
			return (People)super.clone();
	} 
	catch (Exception CloneNotSupportedException) {
	
		}
		return null;
	}	
}

因为在类中如果有某个变量引用了某一个其他变量, 那么在克隆过程中就会出现一个引用了和原对象相同变量的变量, 这样会比较不安全, 所以要在修正克隆方法的同时对这些不安全性进行修正, 比如新建立一个对象, 作为引用传给克隆的对象;

三.lambda表达式

1.基础语法

基础的语法大概是这样的:

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

一个看起来不错的方法是, 可以用lambda表达式来做比较器, 比如之前写的按字符串长度来给字符串数组排序的程序就可以这么写:

class Untitled {
	public static void main(String[] args) {
		String[] s = {"Mustafa", "Samira", "Akhil"};
		for(String i : s) System.out.println(i);
		Arrays.sort(s, (first, second)->first.length()-second.length());
		for(String i : s) System.out.println(i);
	}
}

看起来似乎lambda表达式就是一个语法糖…

猜你喜欢

转载自blog.csdn.net/qq_33982232/article/details/82809589