博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JavaEE - 06类及成员3

Posted on 2020-12-07 21:48  Kingdomer  阅读(108)  评论(0编辑  收藏  举报

JavaSE - 06类及成员3

(1)Object类

java.lang.Object类是所有Java类的根父类。如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类

Object类中的功能(属性、方法)就具有通用性。

属性:无

方法: equals()/ toString()/ getClass()/ hashCode()/ clone()/ finalize()/ wait()/ notify()/ notifyAll()

Object类只声明一个空参的构造器。

public class ObjectTest {
    public static void main(String[] args) {
        Order order = new Order();
        System.out.println(order.getClass().getSuperclass()); //class java.lang.Object
    }
}

Object类的方法

equals

toString()

 

为什么重写 equals时必须重写 hashCode 方法?

  • hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。
  • 这个哈希码的作用是确定该对象在哈希表中的索引位置。
  • hashCode() 定义在 JDK 的 Object.java 中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。
  • 散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码。(可以快速找到所需要的对象)

为什么要有 hashCode?

这里以“HashSet 如何检查重复” 为例子来说明为什么要有 hashCode :
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。
如果两者相同,HashSet 就不会让其加入操作成功。
如果不同的话,就会重新散列到其他位置。
这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

hashCode()与 equals()的相关规定:

如果两个对象相等,则 hashcode 一定也是相同的
两个对象相等,对两个对象分别调用 equals 方法都返回 true
两个对象有相同的 hashcode 值,它们也不一定是相等的
因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

 

 

 

(2)包装类

(2.1)包装类的使用

针对八种基本数据类型定义相应的引用类型 -- 包装类(封装类)
有了类的特点,就可以调用类的方法,这样才是真正的面向对象
 
基本数据类型
包装类
byte
Byte
short
Short
int
Intger
long
Long
float
Float
double
Double
boolean
Boolean
char
Character
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Byte、Short、Integer、Long、Float、Double 的父类: Number

 

 

 

public class WrapperTest2 {
    public static void main(String[] args) {
        Object o1 = true ? new Integer(1) : new Double(2.0);
        System.out.println(o1); //1.0 , 三元运算 Integer 升级到Double

        Object o2;
        if(true){
            o2 = new Integer(1);
        }else{
            o2 = new Double(2.0);
        }
        System.out.println(o2); //1
    }
}

 

 

三目运算符比较基本数据类型,所以在编译阶段自动拆箱为 int 和 double 类型,由于三目运算符要求 表达式2 和 表达式3 类型一致,所以在编译阶段自动类型提升(即 int 自动类型转换为 double 类型),再自动装箱为Object,输出时使用多态调用重写的toString();即Double包装类的toString();

 

int 和 Integer 有什么区别? 

int 是 Java 提供的 8 种原始数据类型之一。
Java 为每个原始类型提供了封装类,Integer 是 Java 为 int 提供的封装类。
int 的默认值为0,而 Integer 的默认值为 null,是引用类型,即 Integer 可以区分出未赋值和值为 0 的区别,int 则无法表达出未赋值的情况
Java 中 int 和 Integer 关系如下:
int 是基本的数据类型;
Integer 是 int 的封装类;
int 和 Integer 都可以表示某一个数值;
int 和 Integer 不能够互用,因为他们两种不同的数据类型;

 

 

(3)抽象类与抽象方法

(3.1)概述

随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。父类描述功能概况,子类完成具体功能的实现。

类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。

(3.2)关键字:abstract

  • abstract 可以用来修饰的结构: 类、方法
  • abstract 不能用来修饰: 属性、构造器等结构;不能用来修饰私有方法、静态方法、final方法、final类
  • abstract修饰类: 抽象类
    • 此类不能实例化
    • 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
    • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作。
    • 抽象的使用前提:继承性
  • abstract修饰方法:抽象方法抽象类是用来(模型化父类无法确定全部实现、而由其子类提供具体实现的对象)的类。
    • 抽象方法只有方法的声明,没有方法体。
    • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的
    • 若子类重写了父类中的所有的抽象方法后,此子类方可实例化
    • 若子类没重写父类中的所有抽象方法,则此子类也是抽象类。需要使用abstract修饰。
    • 超类声明一个方法但不提供实现,该方法的事先由子类提供。这样的方法称为抽象方法。有一个或多个抽象方法的类称为抽象类。

 

public class AbstractTest {
    public static void main(String[] args) {
        //Error:(12, 21) java: com.bearpx.abstractClass.Person是抽象的; 无法实例化
//        Person p1 = new Person();
//        p1.eat();
//Error:(10, 8) java: com.bearpx.abstractClass.Person不是抽象的, 并且未覆盖com.bearpx.abstractClass.Person中的抽象方法eat() //Error:(45, 26) java: 抽象方法不能有主体 // Person p2 = new Person(); // p2.eat(); } }

 

(4)接口(interface)

(4.1)接口概述

  • 有时需要从几个类中派生出一个子类,继承它们所有的属性和方法。但是Java不支持多重继承。有了接口,可以得到多重继承的效果。
  • 有时需要从几个类中抽取出一些共同的行为特征,而它们又不是is-a的关系,仅仅是具有相同的行为特征。
    • 如鼠标、键盘、摄像头、充电器、移动硬盘等都支持USB连接。
  • 接口就是规范,定义的是一组规则,体现现实世界中"如果你是/要...则必须能..."的思想。接口的本质是契约,标准,规范。制定好后大家都要遵守。
    • 继承是一个"是不是"的关系,而接口实现则是"能不能"的关系。
    • 运动员  <-- 篮球运动员 、 跨栏运动员; 学生  <-- 大学生、 中学生; 学习的技能(接口)  <--  跨栏运动员、大学生
    • 可以飞  <-- 飞机、子弹、风筝、热气球; 攻击性 <-- 子弹
  • Java中,接口和类是并列的两个结构。接口是一种特殊的抽象类,只包含常量和方法定义(JDK7及之前),特殊在接口是完全抽象的。

(4.2)如何定义接口

  • 语法: [修饰符] interface 接口名称 {}
  • JDK 7及之前: 只能定义全局常量和抽象方法JDK 8: 除了定义全局变量和抽象方法之外,还可以定义静态方法、默认方法。
    • 全局常量: public static final 
    • 抽象方法: public abstract
  • 接口中没有构造器。也无法被实例化。
  • 接口通过让类去实现(implement)的方式来使用。(面向接口编程)
    • 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化。一个非抽象的类实现接口,需要将所有的方法都实现/重写/覆盖。
    • 如果实现类没有覆盖接口中的所有抽象方法,则此实现类仍是一个抽象类。
  • 一个类可以实现多个接口,这里的实现等同看成继承。弥补了Java单继承性的局限性。接口和接口之间 可以多继承。
    • 格式: class AA extends BB implements CC, DD, EE
    • 格式: interface A {}  interface B{}  interface C extends A,B{}
  • 接口的具体使用, 体现了多态性

 

 

public interface Flyable {
    // 全局变量
    public static final int MAX_SPEED = 7900;
    int MIN_SPEED = 1; // 省略了public static final
    // 抽象方法
    public abstract void fly();
    void stop(); //省略了 public abstract
}
public class InterfaceTest {
    public static void main(String[] args) {
        System.out.println(Flyable.MAX_SPEED);  //7900
        System.out.println(Flyable.MIN_SPEED);  //1
    }
}

USB接口及实现类、Computer调用测试

public interface USB {
    void start();
    void stop();
}
public class UsbTest {
    public static void main(String[] args) {
        Computer com = new Computer();
        // 1. 创建了接口的非匿名实现类的非匿名对象
        Flash flash = new Flash();
        com.transferData(flash);
        // 2. 创建了接口的非匿名实现类的匿名对象
        com.transferData(new Printer());
        // 3. 创建了接口的匿名实现类的非匿名对象
        USB phone = new USB() {
            @Override
            public void start() {
                System.out.println("phone start......");
            }
            @Override
            public void stop() {
                System.out.println("phone stop......");
            }
        };
        com.transferData(phone);
    }
}

排错: px()方法内的 x 需要指定具体的引用对象

 

interface A {
    int x = 0;
}

class B {
    int x = 1;
}
public class C extends B implements A {
    public void pX(){
        //Reference to 'x' is ambiguous, both 'B.x' and 'A.x' match
//        System.out.println(x);
        System.out.println(super.x); //1
        System.out.println(A.x);  //0
    }
    public static void main(String[] args) {
        new C().pX();
    }
}

 

 

 

 

 

(4.3)接口的应用: 代理模式(Proxy)

  • 代理模式就是为其他对象提供一种代理以控制这个对象的访问。
  • 应用场景: 
    • 安全代理: 屏蔽对真实角色的真实访问。
    • 远程代理: 通过代理类处理远程方法调用(RMI)。
    • 延迟加载: 先加载轻量级的代理对象,真正需要再加载真实对象。
      • (大文档查看软件,一个文档中有大的图片,打开文件时,不可能将所有图片都显示,需要查看时,用proxy来进行大图片的打开。)
  • 分类:
    • 静态代理(静态定义代理类)
    • 动态代理(动态生成代理类): JDK自带的动态代理(反射)

 

public interface NetWork {
    public void browse();
}
//------------------------------
// 被代理类
public class Server implements NetWork {
    @Override
    public void browse() {
        System.out.println("真实服务器访问网络");
    }
}

 

// 代理类
public class ProxyServer implements NetWork{
    private NetWork netWork;
    public ProxyServer(NetWork netWork){
        this.netWork = netWork;
    }
    public void check(){
        System.out.println("联网之前的检查工作");
    }
    @Override
    public void browse() {
        check();
        netWork.browse();
    }
}

 

// 接口应用: 代理模式
public class NetWorkTest {
    public static void main(String[] args) {
        Server server = new Server();
        ProxyServer proxyServer = new ProxyServer(server);
        proxyServer.browse();
    }
}

 

明细与经纪人

public class StarProxyTest {
    public static void main(String[] args) {
        Star s = new Proxy(new RealStar());
        s.confer();          // 经纪人面谈
        s.signContract();    // 经纪人签合同
        s.bookTicket();      // 经纪人订票
        s.sing();            // 明星本人唱歌
        s.collectMoney();    // 经纪人收钱
    }
}

 

 

(4.4) JDK8 接口定义静态方法、默认方法

  • 静态方法: 使用static关键字修饰。只能通过接口直接调用静态方法,并执行其方法体。
    • 在相互一起使用的类中使用静态方法。在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。
  • 默认方法: 使用default关键字修饰。通过实现类对象来调用。在已有接口中提供新方法,还保持了与旧版本的兼容性。如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,子类没有重写方法的情况下,默认调用的是父类中的方法。 --> 类优先原则
    • Java8 API 中对Collection、List、Comparator等接口提供了丰富的默认方法。
    • 通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了默认方法,则调用的是重写后的方法。
  • 如果实现类实现了多个接口,多个接口定义了同名同参数的默认方法,在实现类没有重写此方法情况下,报错。--> 接口冲突。
  • 在子类的方法中调用接口被重写的方法: 接口名.super.方法名(参数列表)
public interface CompareA {
    // 静态方法
    public static void method1(){
        System.out.println("CompareA: 北京");
    }
    // 默认方法
    public default void method2(){
        System.out.println("CompareA: 上海");
    }

    default void method3(){
        System.out.println("CompareA: 南京");
    }
}

 

 

 

public class SubClass extends ParentClass implements CompareA, CompareB {
    public void method2(){
        System.out.println("SubClass method2......");
    }
}

 

 

public class MethodTest {
    public static void main(String[] args) {
        SubClass s = new SubClass();
        //Static method may be invoked on containing interface class only
//        SubClass.method1();
        CompareA.method1();   // CompareA: 北京
        //类 SubClass从类型 CompareA 和 CompareB 中继承了method2() 的不相关默认值
        s.method2();          // SubClass method2......
        s.method3();          // 父类method3......
    }
}

 

如何在子类的方法中调用父类、接口中被重写的方法

    // 如何在子类的方法中调用父类、接口中被重写的方法
    public void myMethod(){
        method3();  // 自己定义的重写方法
        super.method3();  // 调用父类方法
        // 调用接口中的默认方法
        CompareA.super.method3();
        CompareB.super.method3();
    }

 

 

 (4.5)抽象类与接口有哪些异同

  • 实现:      抽象类的子类使用extends来继承;    接口必须使用implements来实现接口
  • 构造函数:   抽象类可以有构造函数;             接口没有
  • main方法:  抽象类有main方法,并且可以运行;    接口不能有main方法
  • 实现数量:   类只能继承一个抽象类;             可以实现多个接口。
  • 访问修饰符: 接口中的方法默认使用public修饰符;  抽象类的方法可以是任意访问修饰符

 

 


【接口的作用】
1. 可以使项目分层, 所有的层 都面向接口开发,开发效率提高
灯口 <-- 接口 --> 灯泡 批量生产
架构师 进行 定义接口,

2. 接口使代码之间耦合度降低, 像内存条和主板的关系,可插拔。可以随意切换。

优先接口。
使用接口,保留类的继承 多继承,多实现。
抽象类 单继承。
半抽象
接口能完成的事情,抽象类都能完成。


可扩展能力

-----------------------------

客户业务接口
public interface CustomerService {
// 定义 退出系统的方法
void logout();

}

// 接口的实现类 面向接口去实现的。
public class CustomerServiceImpl {
// 对接口中的抽象方法进行实现
public void logout(){
System.out.println("用户已退出");
}
}

 

public class Test {
// 入口
public static void main(String[] args){
// 执行 CustomerServiceImpl 的 logout 方法。

// 面向接口去调用。
CustomerService cs = new CustomerServiceImpl(); //多态 【父类 子类】

//调用,
cs.logout(); // 编译期看的是接口,运行期看的是对象。
}
}

 

-------------------------------
汽车

生产汽车的厂家面向接口生产。
生产发动机的厂家面向接口生产。

public interface Engine {
//汽车和发动机之间的接口

// 所有的发动机都能启动
void start();

}


Car.java
public class Car{
// Field
// 引擎
// 面向接口编程
Engine e;

Car(Engine e){
this.e = e;
}

public void testEngine(){
e.start(); // 面向接口调用
}

}

 

Yamaha.java
public class Yamaha implements Engine {
public void start(){
System.out.println("雅马哈已经启动");
}

}
Hongda.java
public class Hongda implements Engine {
public void start(){
System.out.println("宏达已经启动");
}

}


public class Test {

public static void main(String[] args){

// 生产引擎
Engine e1 = new Yamaha(); // 多态 父类型引用,指向子类型变量

// 生产汽车
Car c = new Car(e1);

// 测试引擎
c.testEngine();

// 换成宏达
c.e = new Hongda();

c.testEngine();

}
}

 

 

(5)内部类

(5.1)内部类概述

  • 当一个事务的内部,还有一个部分需要一个完整的结构进行描述,在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类
    • 而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类
  • Inner class 一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
    • Inner class的名字不能与包含它的外部类类名相同;
  • 分类:
    • 成员内部类(static成员内部类和非static成员内部类)
    • 局部内部类(不谈修饰符)、匿名内部类 (方法内、代码块内、构造器内)

(5.2)成员内部类

  • 作为外部类的成员:
    • 可以调用外部类的结构
    • 可以被static修饰;可以被4种不同的权限修饰。
  • 作为一个类:
    • 类内可以定义属性、方法、构造器等
    • 可以被final修饰,表示此类不被继承。不使用final,就可以被继承。可以被abstract修饰。
public class Person {
    String name;
    int age = 18;
    public void eat(){
        System.out.println("人: 吃饭");
    }

    //静态成员内部类
    static class Dog{
        String name;
        int age;
        public void show(){
            System.out.println("卡拉是条狗");
//            Person.this.eat();
//            eat();
        }
    }
    // 非静态成员内部类
    class Bird{
        String name;
        public Bird(){}
        public void sing(){
            System.out.println("我是一支小小鸟");
            Person.this.eat(); //调用外部类的非静态属性
            eat();
            System.out.println(age);
        }
        public void display(String name){
            System.out.println(name);              // 方法的形参
            System.out.println(this.name);         // 内部类的属性
            System.out.println(Person.this.name);  // 外部类的属性
        }
    }
}

 

 

 

public class InnerClassTest {
    public static void main(String[] args) {
        // 创建Dog实例(静态成员内部类)
        Person.Dog dog = new Person.Dog();
        dog.show();

        //Person.Bird bird = new Person().Bird();  错误
        // 创建Bird实例(非静态的成员内部类)
        Person p = new Person();
        Person.Bird bird = p.new Bird();
        bird.sing();
        bird.display("wuwu");
    }
}

 

 

 

 

 

(5.3)局部成员类

    
    public void method(){
        class AA{ 
        }
    }
{
class BB{ } }
public Person(){ class CC{ } }

 

    // 返回一个实现了Comparable接口的类的对象
    public Comparable getComparable(){
        // 创建一个实现了Comparable接口的类: 局部内部类
        class MyComparable implements Comparable{
            @Override
            public int compareTo(Object o){
                return 0;
            }
        }
        return new MyComparable();
    }

    public Comparable getComparable2(){
        return new Comparable(){
            @Override
            public int compareTo(Object o){
                return 0;
            }
        };
    }