接口
1. 接口概述
class apple implements Edible{
}
class banana implements Edible{
}
定义apple类和banana类的接口为Edible
Object[] object = {new apple(),new banana()};
if(object[1] instanceof Edible) System.out.println("Yes");
if(object[2] instanceof Edible) System.out.println("Yes");
判断apple类和banana类是否为Edible接口
2. Comparable接口
可以使用Arrays.sort()对Byte、Integer、Long、Double和Float等类进行排序,因为这些类中实现了Comparable接口,Arrays.sort()才可以进行排序,例如:
public class Integer implements Comparable<Integer>{
public int compareTo(Integer o){
//......
}
}
public class Double implements Comparable<Double>{
public int compareTo(Double o){
//......
}
}
实现了Comparable接口才可以进行排序。Byet,Short,Integer,Long,Double,Float,Character,String,BigInteger,BigDecimal,Calendar和Date类都实现了Comparable接口
3. 自定义实现Comparable接口
public class Main{
public static void main(String[] args){
Circle[] nums = {new Circle(5),new Circle(6),new Circle(3)};
Arrays.sort(nums);
for(Circle circle : nums){
System.out.println(circle.getArea() + " ");
}
}
}
class Circle implements Comparable<Circle>{
private double r;
public Circle(){
}
public Circle(double r){
this.r = r;
}
public double getArea(){
return Math.PI * r * r;
}
public double getPerimeter(){
return 2 * Math.PI * r;
}
@Override
public int compareTo(Circle o){
if(getArea() > o.getArea()) return 1;
else if(getArea() < o.getArea()) return -1;
else return 0;
}
}
对Circle类使用implements实现接口Comparable,并在Circle类中重写compareTo方法后,可以使用Arrays.sort()对Circle类的对象根据面积进行排序
在这个例子中,如果不使用接口,很难实现将其根据面积排序
4. Cloneable接口
Cloneable接口的clone方法可用来进行对象拷贝
4.1 clone()方法
ArrayList<Double> list1 = new ArrayList<Double>();
list1.add(3.5);
list1.add(4.5);
list1.add(5.5);
Arraylist<Double> list2 = new ArrayList<Double>();
list2 = (ArrayList<Double>)list1.clone();
ArrayList<Double> list3 = new ArrayList<Double>();
list3 = list1;
list2.add(0.5);
list3.remove(3.5);
System.out.println(list1);
System.out.println(list2);
System.out.println(list3);
输出为:
[4.5,5.5]
[0.5,3.5,4.5,5.5]
[4.5,5.5]
将list1赋值给list3,1和3同时指向一块内存,修改的是同一内存处的数据
list2是拷贝的list1的数据,额外开辟了一块内存,不与1和3共用
4.2 自定义实现Cloneable接口
4.2.1 浅拷贝
public class Main{
public static void main(String[] args) throws CloneNotSupportedException{
House house1 = new House(1,5.2);
House house2 = (House)house1.clone();
System.out.println(house1.id == house2.id);
System.out.println(house1.whenBuild == house2.whenBuild);
}
}
class House implements Cloneable{
public int id;
public double area;
public java.util.Date whenBuild;
public House(int id,double area){
this.id = id;
this.area = area;
whenBuild = new java.util.Date();
}
public void setId(int id){
this.id = id;
}
public void setArea(double area){
this.area = area;
}
public java.util.Date getWhenBuild(){
return whenBuild;
}
public double getArea(){
return area;
}
public int getId(){
return id;
}
@Override
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
输出为:
true
true
注意:main函数处和重写clone处要声明异常
如果clone的数据域是基本类型,则复制它的值
如果clone的数据域是对象,则复制它的引用
这里为house2的area和id额外开辟一块空间,但house2的whenBuild复制的是house1的whenBuild的引用,两者指向同一块内存
所以此时的house1.whenBuild == house2.whenBuild为true
4.2.2 深拷贝
public class Main{
public static void main(String[] args) throws CloneNotSupportedException{
House house1 = new House(1,5.2);
House house2 = (House)house1.clone();
System.out.println(house1.id == house2.id);
System.out.println(house1.whenBuild == house2.whenBuild);
}
}
class House implements Cloneable{
private int id;
private double area;
public java.util.Date whenBuild;
public House(int id,double area){
this.id = id;
this.area = area;
whenBuild = new java.util.Date();
}
public void setId(int id){
this.id = id;
}
public void setArea(double area){
this.area = area;
}
public java.util.Date getWhenBuild(){
return whenBuild;
}
public double getArea(){
return area;
}
public int getId(){
return id;
}
@Override
public Object clone() throws CloneNotSupportedException{
House tempHouse = (House)super.clone();
tempHouse.whenBuild = (java.util.Date)whenBuild.clone();
return tempHouse;
}
}
输出为:
true
false
这里的whenBuild再调用一次clone方法,为house2的whenBuild开辟一块空间,此时house1和house2的whenBuild指向不同的内存,所以house1.whenBuild == house2.whenBuild输出为false
5. 接口和抽象类
接口与抽象类很相似,但接口更灵活,一个类只能继承一个父类,但可以实现多个接口
5.1 抽象类举例
abstract class Animal{
public abstract String howToEat();
}
class Chicken extends Animal{
@Override
public String howToEat(){
System.out.println("Fry it");
}
}
class Duck extends Animal{
@Override
public String howToEat(){
System.out.println("Roast it");
}
}
public static void main(String[] args){
Animal animal = new Chicken();
animal.howToEat();
animal = new Duck();
animal.houToEat();
}
多态可以让你用Animal变量保存Chicken对象或Duck对象
JVM会动态决定调用哪个howToEat()方法
在这里Animal的子类只能是动物类,因为子类必须是父类的一种,而接口就没有此限制
5.2 接口举例
interface Edible{
public String howToEat();
}
class Chicken implements Edible{
@Override
public String howToEat(){
System.out.println("Fry it");
}
}
class Duck implements Edible{
@Override
public String howToEat(){
System.out.println("Roast it");
}
}
class Broccoli implements Edible{
@Override
public String howToEat(){
System.out.println("Stir-fry it");
}
}
public static void main(String[] args){
Edible stuff = new Chicken();
stuff.howToEat();
stuff = new Duck();
stuff.howToEat();
stuff = new Broccoli();
stuff.howToEat();
}
这里的接口多定义了一个西兰花,相对于抽象类限制更少
6. 设计一个Rational类
Rational类用来表示和处理有理数,有理数的分母不能为0,分子可以为0,在这里,负有理数用正分子+负分母表示,一个整数用该整数/1来表示
将Rational类定义为Number类的子类
public class Main{
public static void main(String[] args){
Rational r1 = new Rational(3,-4);
Rational r2 = new Rational(5,7);
System.out.println(r1.add(r2));
System.out.println(r1.sub(r2));
System.out.println(r1.mul(r2));
System.out.println(r1.divide(r2));
System.out.println(r1);
System.out.println(r2);
}
}
class Rational extends Number implements Comparable<Rational>{
private long numerator;
private long denominator;
public Rational(){
this(0,1);
}
public Rational(long a,long b){
long gcd = gcd(a,b);
numerator = (b > 0 ? 1 : -1) * a / gcd;
denominator = Math.abs(b) / gcd;
}
public long getNumerator(){
return numerator;
}
public long getDenominator(){
return denominator;
}
private long gcd(long a,long b){
a = Math.abs(a);
b = Math.abs(b);
long gcdd = 1;
for(int k = 2;k <= a && k <= b;k++){
if(a % k == 0 && b % k == 0)
gcdd = k;
}
return gcdd;
}
public Rational add(Rational a){
long fenmu = denominator * a.getDenominator();
long fenzi = numerator * a.getDenominator() + a.getNumerator() * denominator;
return new Rational(fenzi,fenmu);
}
public Rational sub(Rational a){
long fenmu = denominator * a.getDenominator();
long fenzi = numerator * a.getDenominator() - a.getNumerator() * denominator;
return new Rational(fenzi,fenmu);
}
public Rational mul(Rational a){
long fenmu = denominator * a.getDenominator();
long fenzi = numerator * a.getNumerator();
return new Rational(fenzi,fenmu);
}
public Rational divide(Rational a){
long fenmu = denominator * a.getNumerator();
long fenzi = numerator * a.getDenominator();
return new Rational(fenzi,fenmu);
}
@Override
public String toString(){
String str = "";
if(denominator == 1)
str = numerator + "";
else
str = numerator + "/" + denominator;
return str;
}
@Override
public int CompareTo(Rational a){
if(this.sub(a).getNumerator() > 0) return 1;
else if(this.sub(a).getNumerator() < 0) return -1;
else return 0;
}
}
- 调用无参构造方法,创建一个分子为0,分母为1的有理数
- 调用重载的构造方法,先用gcd判断其最大公约数,然后将该有理数除以最大公约数化为最简形式,再将分母的符号传给分子,保证负有理数的负号在分子上
- 重写toString方法和CompareTo方法
输出结果为:
-1/28
-41/28
-15/28
-21/20
-3/4
5/7