文章目录
一、抽象类
1.1 什么是抽象类
当无法准确地描述该方法/类的内容时,就可以用abstract修饰
没有实际工作的方法(无法准确描述), 我们可以把它设计成一个 抽象方法,包含抽象方法的类我们称为抽象类
1.2 抽象类的注意点
- 抽象类和抽象方法都是依靠abstract进行修饰的
abstract class Father{ //抽象类
abstract public void func(); //抽象方法
}
- 抽象类不能进行实例化(因为抽象类自己本身无法准确描述一个类),但是普通类可以
abstract class Father{ //抽象类
abstract public void func(); //抽象方法
}
public class Test1{
public static void main(String[] args) {
//Father father = new Father(); 会报错
}
}
- 抽象类当中,不一样包含抽象方法,但是包含抽象方法的类,一定是抽象类
- 抽象类当中可以存在构造方法,在子类实例化的时候,会帮助父类的成员进行初始化
- 抽象类当中,可以定义成员变量和成员方法
- 当一个普通类,继承我们的抽象类了,此时在普通类当中一定要重写抽象类的抽象方法(不重写会报错,所以抽象类最大的意义是为了被继承)
- 当一个抽象类A继承一个抽象类B,此时抽象类A不需要重写抽象类B中的成员。但是当一个普通类C,继承了抽象类A,此时就需要重写所有没有被重写的抽象方法
- 一定要满足重写的要求,所以final 关键字不可能同时作用在一个方法或者类上
1.3 抽象类的作用
- 抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法,从而可以达到多态的效果
- 使用抽象类的场景实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题,即可以进行一次编译器的检验
二、接口
2.1 接口的基础概念
(1)什么是接口
生活中,我们有许许多多的接口,插座插头、USB接口……因为口径尺寸相同,是个标准的规范,我们可以很方便地去使用。
类比一下
接口其实就是一个对标准的规范,可以算抽象类的进一步抽象
(2)如何使用接口
语法格式:interface 接口名称;
接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口
写法上的注意点:
interface Ishape{ //接口名推荐以I来开头
public abstract void draw(); //public abstract这块是灰色,即接口中的方法默认是public abstract的
//阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.
//接口当中有不能实现的方法,两种例外
public static void eat(){ //静态方法可以有具体的实现
System.out.println("中午吃什么?");
}
default public void drink(){ //这个方法被 default 关键词修饰,表示这个方法默认就是这样的
System.out.println("中午喝什么?");
}
int a = 1;
public static int b = 1; //public static 是灰色
public static final int c = 1; //public static final 是灰色,
// 即接口当中的成员变量默认是public static final修饰的
}
public class Demo {
public static void main(String[] args) {
//Ishape shape = new Ishape(); 会报错,接口不能通过关键词new来初始化
}
}
子类实现接口的方法的时候,这个方法一定要是public修饰的
接口当中,不能有构造方法和代码块
示例代码:
interface IShape{
void draw();
public static void eat(){
}
}
class Cycle implements IShape { //类和接口之间,使用关键词implements进行关联
//重写父类的方法
//当一个类实现一个接口之后,这个类必须重写这个接口当中的抽象方法
@Override
public void draw() {
System.out.println("○");
}
//当接口当中,存在default方法,可以选择重写,也可以选择不重写,具体看业务需求
public void eat(){
System.out.println("在重写这个default修饰的方法");
}
}
class Rect implements IShape {
@Override
public void draw() {
System.out.println("▭");
}
}
public class Demo{
//可以实现多态
public static void drawMap(IShape iShape){
iShape.draw();
}
public static void main(String[] args) {
IShape iShape = new Cycle(); //接口虽然不能实例化自己,但可以实例化其子类,这个过程也是向上转型
drawMap(new Cycle());
drawMap(new Rect()); //ps.接口有自己独立的字节码文件
}
}
2.2 实现多个接口
意义:可以实现多继承,不是所有的子类都可以
interface IFlying{
void fly();
}
interface ISwimming{
void swim();
}
interface IRunning{
void run();
}
abstract class Animal{
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void eat();
}
class Dog extends Animal implements ISwimming, IRunning{ //先继承类,再关联接口,不能改变顺序
public Dog(String name, int age) { //这里接口的意义,不是所有的动物都会飞
super(name, age);
}
@Override
public void eat() {
System.out.println(this.name + "正在吃狗粮");
}
@Override
public void swim() {
System.out.println(this.name + "正在河里游泳");
}
@Override
public void run() {
System.out.println(this.name + "正在遛人");
}
}
class Bird extends Animal implements IFlying, IRunning{
public Bird(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(this.name + "正在吃鸟粮");
}
@Override
public void fly() {
System.out.println(this.name + "正在吃天空飞翔");
}
@Override
public void run() {
System.out.println(this.name + "正在吃地上蹦蹦跳跳");
}
}
public class Main {
public static void testAnimal(Animal animal){
animal.eat();
}
public static void testRun(IRunning run){
run.run();
}
public static void main(String[] args) {
testAnimal(new Bird("小鸟",1));
testRun(new Dog("小狗",2));
}
}
2.3 接口的继承
interface A {
void testA();
}
interface B {
void testB();
}
//接口和接口之间也存在关联,通过extends来进行关联,此时认为这个意思是扩展功能!
interface C extends A,B { //此时的C接口,不仅仅有自己的功能,还有A接口和B接口的功能,
void testC(); //可以使得代码的耦合更低,不会改变一个代码,会影响另一个代码,彼此之间相互独立
}
class D implements C {
@Override
public void testA() {
}
@Override
public void testB() {
}
@Override
public void testC() {
}
}
2.4 常用接口实例
(1)Comparable 接口 ------- 排序
当我们对自定义类型进行比较的时候,一定要实现可比较的接口
- Comparable 接口 里面有一个抽象方法是compareTo
- 自定义类型中我们如果要排序,需要关联上这个接口,再重写compareTo方法
public class Person implements Comparable<Person>{
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person o) {
return o.age - this.age;
}
}
public class Main{
public static void main(String[] args) {
Person[] person = new Person[3];
person[0] = new Person("张三",21);
person[1] = new Person("李四",8);
person[2] = new Person("王五",76);
System.out.println("排序前:"+ Arrays.toString(person));
Arrays.sort(person);
System.out.println("排序后:"+ Arrays.toString(person));
}
}
✨自写排序
public class Person implements Comparable<Person>{
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person o) {
return this.age - o.age;
}
}
public class Main{
public static void main(String[] args) {
Person[] person = new Person[3];
person[0] = new Person("张三",21);
person[1] = new Person("李四",8);
person[2] = new Person("王五",76);
System.out.println("排序前:"+ Arrays.toString(person));
bubberSort(person);
System.out.println("排序后:"+ Arrays.toString(person));
}
public static void bubberSort(Comparable[] comparables){
for (int i = 0; i < comparables.length - 1; i++) {
for (int j = 0; j < comparables.length - 1 - i; j++) {
if (comparables[j].compareTo(comparables[j + 1]) > 0){
Comparable tmp = comparables[j];
comparables[j] = comparables[j+1];
comparables[j+1] = tmp;
}
}
}
}
}
(2)Compactor 接口 ------- 排序
使用场景: Compactor 的 compareTo 把排序的对象写死了,即无法通过 name 排序,相比于 compareTo , Compactor 更加的灵活,可以从不同条件来进行排序
class Student {
public String name;
public int age;
public double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
class ScoreComparator implements Comparator<Student> {
public int compare(Student o1, Student o2) {
return (int)(o1.score - o2.score);
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student("zhangsan",19,30.9);
Student student2 = new Student("lisi",9,80.9);
AgeComparator ageComparator = new AgeComparator();
int ret = ageComparator.compare(student1,student2);
System.out.println(ret);
System.out.println("+++++++++++++++++++++");
ScoreComparator scoreComparator = new ScoreComparator();
int ret2 = scoreComparator.compare(student1,student2);
System.out.println(ret2);
}
}
(3)Cloneable 接口 ------- 拷贝
class Stu implements Cloneable{ //标记接口,里面什么都没有,关联了该接口,证明该类是可以被克隆的
private String name;
private int age;
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException { //处理异常问题
return super.clone();
}
}
public class Clone { //处理异常问题
public static void main(String[] args) throws CloneNotSupportedException{
Stu student1 = new Stu("aaa", 19);
Stu student2 = (Stu)student1.clone(); //clone返回的方法是Object类的,所以要强转
System.out.println(student1);
System.out.println(student2);
}
}
❤️浅拷贝 VS 深拷贝
拷贝的时候,能把这个对象里的所有内容都拷贝过来,则是深拷贝。否则,是浅拷贝
下面这个是浅拷贝
class Money{
public int money;
}
class Stu implements Cloneable{
private String name;
private int age;
Money m = new Money();
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Clone {
public static void main(String[] args) throws CloneNotSupportedException{
Stu student1 = new Stu("aaa", 19);
Stu student2 = (Stu)student1.clone();
student1.m.money = 19;
System.out.println(student1.m.money);
System.out.println(student2.m.money);
System.out.println();
student1.m.money = 29;
System.out.println(student1.m.money);
System.out.println(student2.m.money);
}
}
下面是改成深拷贝的情况
核心思想:每个对象都要拷贝
class Money implements Cloneable{
public int money;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Stu implements Cloneable{
private String name;
private int age;
Money m = new Money();
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Stu tmp = (Stu)super.clone();
tmp.m = (Money) this.m.clone();
return tmp;
}
}
public class Clone {
public static void main(String[] args) throws CloneNotSupportedException{
Stu student1 = new Stu("aaa", 19);
Stu student2 = (Stu)student1.clone();
student1.m.money = 19;
System.out.println(student1.m.money);
System.out.println(student2.m.money);
System.out.println();
student1.m.money = 29;
System.out.println(student1.m.money);
System.out.println(student2.m.money);
}
}
三、Object类
1.概念
可以认为Object这个类是所有类的父类,那怕你没有显示地继承这个类
Java当中只能同时继承一个类
在 Java 中,只有基本类型 ( primitive types) 不是对象, 例如,数值、 字符和布尔类型的值都不是对象。
所有的数组类塱,不管是对象数组还是基本类型的数组都扩展了 Object 类
2.使用
Object 中的方法:
1.引用任何实际的对象
Object obj = new EmployeeC’Harry Hacker", 35000);
当然, Object 类型的变量只能用于作为各种值的通用持有者。要想对其中的内容进行具体的操作, 还需要清楚对象的原始类型, 并进行相应的类型转换:
Employee e = (Employee) obj ;
3.1 toString() ---------- 返回表示对象值的字符串
如果要打印自定义对象中的内容,可以直接重写Object类中的toString()方法
//Object 类定义了 toString 方法, 用来打印输出对象所属的类名和散列码
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
- 绝大多数(但不是全部)的 toString方法都遵循这样的格式:类的名字,随后是一对方括号
- 随处可见 toString方法的主要原因是:只要对象与一个字符串通过操作符“
+” 连接起来,Java 编译就会自动地调用 toString方法,以便获得这个对象的字符串描述 - 数组继承了Object中的toString()方法,数组类型将按照旧的格式打印。修正的方式是调用静态方法 Arrays.toString()。
要想打印多维数组(即, 数组的数组)则需要调用 Arrays.deepToString 方法
3.2 对象比较equals方法
比较对象内容时,一定要根据实际,重写equals方法
// Object类中的equals()方法实现:
public boolean equals(Object obj) {
return (this == obj);
}
3.3 hashcode方法 --------->算了一个具体的对象位置
1、hashcode方法用来确定对象在内存中存储的位置是否相同
2、事实上hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
// Object类中的hashcode()方法实现:
public native int hashCode();
//native表示这个方法,底层是由C/C++代码写的。我们看不到
假如我们认为两个名字相同,年龄相同的对象,将存储在同一个位置,并要实现这个效果
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class TestDemo4 {
public static void main(String[] args) {
Person per1 = new Person("gaobo", 20) ;
Person per2 = new Person("gaobo", 20) ;
System.out.println(per1.hashCode());
System.out.println(per2.hashCode()); //执行结果一样
}
}
equals方法 和 hashcode方法都可以用快捷键重写