Surpass Day——Java 接口在开发中的作用、关于Object类、内部类

98 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

1、接口在开发的作用

类似于多态在开发中的作用;

  • 多态:面向抽象编程(面向接口编程),不要面向具体编程。降低程序的耦合度,提高程序的扩展力,符合OCP的开发原则;
  • 接口的使用离不开多态机制(接口+多态才可达到解耦合);
  • 接口可以解耦合,解的是调用者实现者的耦合;
  • 调用者面向接口调用,实现者面向接口实现

以去餐馆吃饭为例:

接口是抽象的,菜单是一个接口(菜单上有一个抽象的照片:西红柿炒鸡蛋)

谁面向接口调用?(顾客面向菜单点菜,调用接口)

谁负责实现这个接口呢?(后台的厨师负责把西红柿炒鸡蛋做好,是接口的实现者)

这个接口有什么用呢?(有这个饭馆的"菜单",让“顾客”和“后厨”解耦合了,他们完全依靠这个抽象的菜单沟通)

public class Super {
    public static void main(String[] args) {
        //创建厨师对象
        Meau c1 = new ChineseCook();
        //创建顾客对象
        Customer customer = new Customer(c1);
        //顾客点菜
        customer.order();
    }
}
interface Meau{
    void ChaoDan();
    void RouSi();
}
class Customer{
    //Customer has a Meau!顾客手里有一个菜单
    //实例变量:属性
    private Meau meau;
    //提供点菜的方法
    public void order(){
        //调用get方法或直接访问先拿菜单
        Meau m = this.getMeau();
        m.ChaoDan();
        m.RouSi();
        //meau.RouSi();
        //meau.ChaoDan();
    }
​
    public void setMeau(Meau meau) {
        this.meau = meau;
    }
​
    public Meau getMeau() {
        return meau;
    }
​
    public Customer() {
    }
​
    public Customer(Meau meau) {
        this.meau = meau;
    }
//如果以下这么写就写死了,焊死了,没有可插拔了
    //ChineseCook c1;
    //JapaneseCook c2;
}
class ChineseCook implements Meau{
    public void ChaoDan(){
        System.out.println("中餐师傅做炒蛋");
    }
    public void RouSi(){
        System.out.println("中餐师傅做肉丝");
    }
}
class JapaneseCook implements Meau{
    public void ChaoDan(){
        System.out.println("日本师傅做炒蛋");
    }
    public void RouSi(){
        System.out.println("日本师傅做肉丝");
    }
}


2、继承、关联、实现

is a:继承
Cat is a Animal;(猫是一个动物)
凡是满足is a的表示都可以设置为“继承”;
A extends B
has a:关联
I has a Animal;(我有一支笔)
凡是能够用has a来描述的,统一以“属性(关联)”的方式存在;
A{
    B b;
}
like a:实现
Cook like a FoodMeau;(厨师像一个菜单一样)
凡是能够满足like a关系的表示类“实现”接口;
A implement B

3、抽象类和接口的语法区别

抽象类是半抽象的; 接口是完全抽象的;

抽象类中有构造方法; 接口中没有构造方法;

接口和接口之间支持多继承; 一个抽象类只能继承一个类(单继承);

接口中只允许出现常量和抽象方法;

注意:以后接口使用的比抽象类多,接口一般都是对“行为”的抽象


4、关于Object类

4.1 什么是ApI?

  • 应用程序编程接口;
  • 整个JDK的类库就是一个javase的API;
  • 每一个API都会配置一套API帮助文档;
  • SUN公司提前写好的这套类库就是API(一般每一份API都对应一份API帮助文档);

4.2 Object类中的方法

protected Object clone() //负责对象克隆的

int hashCode() //获取对象哈希值的一个方法

boolean equals(Object obj) //判断两个对象是否相等

String toString() //将对象转换成字符串形式

protected void finalize() //垃圾回收器负责调用的方法

4.3 关于toString方法

1)源代码

public String toString(){
    return this.getClass().getName() + "@" + Integar.toHexString(hashCode());
}
//源代码上toString()方法的默认实现是:
//类名@对象的内存地址转换为十六进制的形式

2)作用:

调用toString方法可以将一个"java对象"转换成字符串表示形式

3)建议所有子类都去重写toString()方法,toString()方法应该是一个简洁的、详实的、易阅读的

public class Hello{
    public static void main(String[] args) {
        Time t1 = new Time(1970,1,1);
        String s1 = t1.toString();
        System.out.println(s1);
        //System.out.println(t1);
    }
}
class Time{
    int year;
    int month;
    int day;
    public Time(){
​
    }
    public Time(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
    //重写之前:输出Time@a09ee92
    public String toString(){
        return this.year + "年" +this.month+ "月" + this.day + "日";
    }//重写之后:输出1970年1月1日
}

4.4 关于equals方法

1)源代码

public boolean equals(object obj){
    return (this == obj);
}

以上这个方法是Object类的默认实现,默认采用==判断两个java对象是否相等,而==判断的是java对象的内存地址是否相等,需要重写equals

2)作用:

判断两个java对象是否相等

3)代码表示

public class Hello{
    public static void main(String[] args) {
        Time t1 = new Time(2008,8,8);
        Time t2 = new Time(2008,8,8);
        System.out.println(t1 == t2);//这是比较的是两个对象保存的内存地址是否相等?
        boolean b = t1.equals(t2);
        System.out.println(b);
        //System.out.println(t1.equals(t2));
    }
}
class Time {
    int year;
    int month;
    int day;
​
    public Time() {
​
    }
​
    public Time(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
​
    //重写equals方法
    public boolean equals(Object obj) {
        //如果obj为空,如果obj不是Time类型没必要比较,直接返回false
        if (obj == null||!(obj instanceof Time))
            return false;
        //如果obj保存的地址相同没必要比较,直接返回true
        if (this == obj) {
            return true;
        }
        //这里直接向下转型,obj一定是Time类型
        Time t = (Time) obj;
        return this.year == t.year && this.month == t.month && this.day == t.day;
    }
}

4)String类已经重写了equals和toString方法,String中存在构造方法且字符串比较不能用"=="

5)基本数据类型比较能使用"==",所有的引用数据类型比较需要调用equals方法;

4.5 关于finalize()方法

1)源代码

protected void finalize() throws Throwable{ }

2)finalize()方法只有一个方法体里面没有代码,而且这个方法是protected修饰的;

3)这个方法不需要程序员手动调用,JVM的垃圾回收负责调用这个方法;

4)finalize()方法实际上是SUN公司为java程序员准备的一个时机,垃圾销毁时机,如果希望在对象销毁的时机执行一段代码的话,这段代码要写到finalize()方法当中;

5)

public class Gc {
    public static void main(String[] args) {
        Peason p = new Peason();
        p = null;
    }
}
class Peason{
    protected void finalize() throws Throwable{
        System.out.println("对象垃圾即将被销毁!!!");
    }
}

java中的垃圾回收器不是轻易启动的,垃圾太少,或者时间没到,种种条件下可能启动也可能不启动;

System.gc();//可以建议垃圾回收器启动,但不一定启动;

4.6 关于hashCode方法

1)源代码

public native int hashCode();

2)这个方法不是抽象方法,带有native关键字,底层调用C++程序;

3)hashCode()方法返回的是哈希码;

实际上就是一个java对象的内存地址,经过哈希算法得出一个值,所以hashCode()方法的执行结果可以等同看做一个java对象的内存地址

4)

public class HashCode{
    public static void main(String[] args) {
        Object o = new Object();
        int hashCOdeValue = o.hashCode();
        System.out.println(hashCodeValue);
    }
}


5、内部类

5.1 定义

内部类:在类的内部又定义了一个新的类,被称为内部类

5.2 内部类的分类

静态内部类:类似于静态变量;

实例内部类:类似于实例变量;

局部内部类:类似于局部变量;匿名内部类属于局部内部类的一种(没有名字)

5.3 代码解释

class Test{
    //静态内部类
    static class Inner1{
    }
    //实例内部类
    class Inner2{
    }
    public void doSome(){
        //局部变量
        int i = 10;
        //局部内部类
        class Inner3{
        }
    }
    public void doOther(){
        new Test.new Inner2//这样使用
        //doSome()方法中的内部类Inner3,在doOther()中不能用
    }
}

使用内部类编写的代码可读性很差

5.4 匿名内部类

是局部内部类的一种,没有名字

public class Test {
    public static void main(String[] args) {
        MyMath mm = new MyMath();
        //ComputerImp c = new ComputerImp()
        //mm.mySum(c,100,200);
        //以上两行代码可以直接换成下面一行
         mm.mySum(new ComputerImp(),100,200);
    }
}
//负责计算的接口
interface Computer{
    //抽象方法
    int sum(int a,int b);
}
//编写一个Computer接口的实现类用来创建对象
class ComputerImp implements Computer{
    //对方法的实现
    public int sum(int a,int b){
        return a + b;
    }
}
//数学类
class MyMath{
    //数学求和方法
    public void mySum(Computer c,int x,int y){
        int retValue = c.sum(x,y);
        System.out.println(x + "+" + y + "=" + retValue);
    }
}

使用匿名内部类之后

public class Test {
    public static void main(String[] args) {
        MyMath mm = new MyMath();
        mm.mySum(new Computer(){
            int sum(int a,int b){
                return a + b;
            }
        },100,200);//这里并不是接口可以直接new了,中间的{}代表了对接口的实现
    }
}
interface Computer{
    int sum(int a,int b);
}
class ComputerImp implements Computer{
    //对方法的实现不用写了,用匿名内部类实现
}
//数学类
class MyMath{
    //数学求和方法
    public void mySum(Computer c,int x,int y){
        int retValue = c.sum(x,y);
        System.out.println(x + "+" + y + "=" + retValue);
    }
}

不建议用匿名内部类,因为没有名字,没办法重复使用,代码太乱,可读性太差

注意:使用IDEA自动写匿名内部类

mm.mySum(new Computer(){},写完这个之后,需要纠正错误:

光标移动到红色下划线下面,快捷键:Alt + 回车