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
|
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 和 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;
}
};
}