一.抽象类
抽象类被abstract修饰,抽象类当中可以含有抽象类方法,抽象类的方法不能写大括号也可以含有非抽象类方法,抽象类方法在抽象类当中不具体实现,但是抽象类被继承以后,需要在子类中重写。
- abstract class Xing
- {
- String name;
- abstract public void draw(); //被abstract修饰的方法称为抽象方法
- abstract void count();
- Xing(String name)
- {
- this.name=name;
- }
-
- }
- class Cycle extends Xing
- {
- //String name;
- String silly;
- public Cycle(String name,String silly)
- {
- super(name);
- this.silly=silly;
- }
- int age;
- public void draw()
- {
- System.out.println(name+"正在画三角形");
-
- }
- void count()
- {
- System.out.println(name+"正在数三角形");
- }
-
- }
2.抽象类方法在没有修饰限定访问符时,默认为public,但是它的修饰限定访问符不能是private,也不能被final,static修饰,因为它需要在子类中重写。
3.抽象类不能创建新的对象
4.抽象类里可以含有普通方法和属性。
5.继承抽象类的类在重写抽象类里的方法时,方法的修饰符必须要大于等于抽象类里的抽象方法的修饰符
二.Object类
1.Object是所有类的父类
- public static void main2(String[] args) {
- Object e = new Person();
- }
2.Object类中的 equals方法
我们先来看一段代码
- public static void main(String[] args) {
- Dog dog1=new Dog("wang",7);
- Dog dog2=new Dog("wang",7);
- System.out.println(dog1.equals(dog2));
-
- }
大家先来猜一下这段代码它最终打印出来的结果?因为两个对象的内容一样,我相信有一部分人会猜测结果是true,

那么为什么会是这样一种结果呢?我们可以点进去看一下Object类中的equals方法的具体实现

我们不难看到其实比较的是dog1和被dog2赋值的obj这两个引用的值,那么它们肯定是不相等的。
我们可以在子类中将这个方法按照自己的想法进行重写,
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (!(obj instanceof Dog)) {
- return false;
- }
- Dog a = (Dog)obj;
- if (this.name.equals(a.name) && this.age == a.age) { 8
- return true;
- }
- }
有同学可能会问8行中的equals方法不还是比较两个引用的值吗?其实这里的equals方法不是Object中的equals方法了,它被底层代码进行了重写。
3.hasCode 方法
hasCode方法可以计算出一个对象具体的位置
我们可能会认为两个对象内容一样,那么他们的地址就是相同的
- public class Text1 {
- public static void main(String[] args) {
- Animal dog1=new Animal("小红",5);
- Animal dog2=new Animal("小红",5);
- System.out.println(dog1.hashCode());
- System.out.println(dog2.hashCode());
-
- }
- }
我们再看一下打印结果:

但是我们可以看到即使这两个对象的内容完全相同,但是他们的地址也不相同,像重写toString()方法一样,我们重写一下hascode()方法。
- public class Animal {
- public String name;
- public int age;
-
- public Animal(String name, int age) {
- this.name = name;
- this.age = age;
- }
-
- @Override
- public int hashCode() {
- //return super.hashCode();
- return Objects.hash(name,age);
- }
- }
我们再来看一下打印结果

结论:1.hashCode()方法用来确定对象在内存中存储的位置是否相同
2.hasCode()在散列表中才有用,在其它请况下没用,在散列表中hashCode()的作用是获取对象的散列码,进而确定该对象在该散列表中的具体位置。
三.接口
1.接口的形式通过关键字interface来定义
- interface name
- {
- void eat();
- void fly();
- void swim(); //这三个都被默认成public abstract
- int b=3; //这个被默认成public static final
- }
2.接口里面所有的方法都必须是抽象的方法,方法都被默认成了public abstract,接口里不能有静态代码块和构造方法,可以有变量,变量会被默认成public static final的形式
3.接口需要被类实现。所有接口里的方法都需要在类中被重写,重写的时候不能使用default做修饰访问限定符
4.类中需要实现该接口的时候用implements
- interface Swim
- {
- void swim();
-
-
-
- }
- class Fish extends Animal implements Swim
- {
- //String name="qin";
- public Fish(String name)
- {
- super(name);
- }
- public void swim()
- {
- System.out.println(name+"正在游泳");
- }
- }
可以一次连接多个接口 用,间隔
- interface Swim
- {
- void swim();
-
-
-
- }
- interface Run
- {
- void run();
- }
-
-
-
-
-
- class Dog extends Animal implements Run,Swim
- {
- //String name="wang";
- public Dog(String name)
- {
- super(name);
- }
- public void run()
- {
-
- System.out.println(name+"正在跑");
- }
- public void swim()
- {
- System.out.println(name+"正在游泳");
- }
-
-
- }
6.默认方法
可以为接口方法提供一个默认实现,但必须用default修饰这样一个方法
- interface Swim
- {
- default void run()
- {
- System.out.println(111);
- }
-
-
-
- }
解决默认方法冲突
那么当一个超类和一个接口都实现了同一个方法时,在这种情况下只会考虑超类方法,接口的默认方法被忽略。
- interface Run
- {
- default void run()
- {
- System.out.println(333);
- }
- }
- class Animal
- {
- public void run()
- {
- System.out.println(222);
-
- }
- class Dog extends Animal implements Run
- {
- String name="wang";
-
-
- }
-
-
-
-
- public class text6 {
- public static void main(String[] args) {
- Dog dog=new Dog();
- dog.run();
- //dog.eat();
- }

当两个接口都实现了同一个默认的方法,我们用哪个就在类里重写哪个
- interface Swim
- {
- default void run()
- {
- System.out.println(111);
- }
-
-
-
- }
- interface Run
- {
- default void run()
- {
- System.out.println("接口里的方法");
- }
- }
- class Dog implements Run,Swim
- {
- String name="wang";
-
-
-
- public void run()
- {
- System.out.println(111);//重写的是Swim中的方法
- }
- }
- public class text6 {
- public static void main(String[] args) {
- Dog dog=new Dog();
- dog.run();
- //dog.eat();
- }
- }

7.在类实现这个接口,并重写这个接口的抽象方法时,这个重写的方法必须是public修饰的,因为我们可以把接口当成一个特殊的类,那么实现这个接口的类就相当于是子类,接口就是父类,当子类要重写父类的方法时,方法修饰符必须要大于等于父类方法的修饰符,因为接口类里的方法时public abstract,所以实现这个接口的类里的方法必须是public。
8.接口里不能有构造方法(接口不能被实例化)和静态代码块
9.接口里可以含有具体实现的静态方法
- public interface IEO {
- void draw();
- default public void cut()
- {
- System.out.println("接口里默认提供的方法");
- }
- public static void fun()
- {
- System.out.println("接口里可以含有静态的方法静态方法");
- }
- }
10.接口可以当成是一个特殊的类,多个类去实现这个接口,就相当于继承这个类 ,也就意味着可以发生多态

四.两个重要的接口:
1 .comparable
我们大家先来看一段代码:
- public static void main1(String[] args) {
- Students[]arr=new Students[3];
- arr[0]=new Students("wang",13);
- arr[1]=new Students("xiao",14);
- arr[2]=new Students("xiang",15);
- sort(arr);
- System.out.println(Arrays.toString(arr));
-
-
像这段代码如果不加任何接口我们是无法对其排序的,因为编译器无法识别到底是按照name排序还是按照年龄排序,这时我们可以用一下comparable这个接口,使Students这个类具备可以比较的功能。
- class Students implements Comparable<Students>
- {
- String name;
- int age;
- public Students(String name, int age)
- {
- this.name=name;
- this.age=age;
- }
-
- @Override
- public String toString() {
- return ("["+name+","+age+"]");
- }
-
- @Override
- public int compareTo(Students o) {
- if(this.age-o.age>0)
- {
- return 1;
- }
- else if(this.age-o.age<0)
- {
- return -1;
-
- }
- else
- {
- return 0;
- }
-
-
-
-
- }
-
我们可以按照年龄排序,这是我们就在这个类里面重写comparaTo这个方法,这是我们就可以排序了。

那么大家再来想一个问题,如果我们按照姓名来比较呢?那么势必我们就需要改动刚刚重写的按照年龄排序的compareTo方法,这很不方便,那么接下我我就给大家介绍一个比较器,这个就用起来很方便了。
按照年龄比较
- class Namecomparator implements Comparator<Students>
- {
- @Override
- public int compare(Students o1, Students o2) {
- return (o1.name.compareTo(o2.name));
- }
- }
- class Students
- {
- String name;
- int age;
- public Students(String name, int age)
- {
- this.name=name;
- this.age=age;
- }
-
- @Override
- public String toString() {
- return ("["+name+","+age+"]");
- }
- }
- public static void main(String[] args) {
- Students[]arr=new Students[3];
- arr[0]=new Students("li",16);
- arr[1]=new Students("jing",17);
- arr[2]=new Students("rui",18);
- Agecomparator age= new Agecomparator();// 创建一个Agecomparator对象
- //Namecomparator age2=new Namecomparator();
-
-
-
- sort(arr,age); //注意这里传的是两个引用,一个是Students类的,另一个Agecomparator对象的引用
- System.out.println(Arrays.toString(arr));
-
- }
-

按照姓名比较
- class Namecomparator implements Comparator<Students>
- {
- @Override
- public int compare(Students o1, Students o2) {
- return (o1.name.compareTo(o2.name));
- }
- }
- class Students
- {
- String name;
- int age;
- public Students(String name, int age)
- {
- this.name=name;
- this.age=age;
- }
-
- @Override
- public String toString() {
- return ("["+name+","+age+"]");
- }
- }
- public static void main(String[] args) {
- Students[]arr=new Students[3];
- arr[0]=new Students("li",16);
- arr[1]=new Students("jing",17);
- arr[2]=new Students("rui",18);
- //Agecomparator age= new Agecomparator();
- Namecomparator age2=new Namecomparator();//创建一个Namecomparator 的对象
-
-
-
- sort(arr,age2);//传两个引用
- System.out.println(Arrays.toString(arr));
- }

第二个接口:cloneable
1.浅拷贝
- class Math implements Cloneable
- {
- public int n=11;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- }
- class Person implements Cloneable
- {
- public int m=10;
- public Math math=new Math();
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- public static void main(String[] args) throws CloneNotSupportedException{
- Person person2=new Person();
- person2.math.n=19;
- Person person3=(Person)(person2.clone());
-
- System.out.println(person2.math.n);
- System.out.println(person3.math.n);
-
- }

那么为什么会是这样一种结果呢?我们不妨一起来分析一下

我们看到当我们person1的对象克隆以后,但是math的值没有变,还是指向n=11,所以person2.math.n修改为19的时候,person3.math.n的值也会成为19,我们并不希望它们的结果都改变,所以我们考虑一下深拷贝。

我们先创建一个tmp变量用来引用克隆person1后的后的对象,然后我们在克隆一下math的对象。这时math的值就改变了,然后再把tmp的值赋值给person2,

这时通过person2把n的值改为19,person1的n就不会改变了
- class Math implements Cloneable
- {
- public int n=11;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- }
- class Person implements Cloneable
- {
- public int m=10;
- public Math math=new Math();
-
-
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- Person tmp=(Person)(super.clone());
- tmp.math= (Math) (this.math.clone());
- return tmp;
- }
- }
- public class text10 {
- public static void main(String[] args) throws CloneNotSupportedException{
- Person person1=new Person();
- Person person2=(Person)(person1.clone());
- person2.math.n=19;
- System.out.println(person1.math.n);
- System.out.println(person2.math.n);
-
-
-
-
- }

五.为什么重写equals方法还要重写hashcode方法?
通常情况下,我们重写equals方法,如果两个对象属性值相同,那么我们就认为它们两个对象是同一个对象,但是这两个对象的hash值不一样,如果我们把他们放在set容器中,两个对象都可以放进去,可是set容器最大的特性就是去重,明明我们认为这是两个相同的对象,这就会产生问题,
举个例子:我们建立一个Student类,然后重写equals方法
- public class Student {
- public String name;
- public int age;
-
- public Student(String name, int age) {
- this.name = name;
- this.age = age;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Student student = (Student) o;
- return age == student.age && Objects.equals(name, student.name);
- }
-
- /*@Override
- public int hashCode() {
- return Objects.hash(name, 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;
- }
- }
- Student student=new Student("小海",1);
- Student student1=new Student("小海",1);
- System.out.println(student.hashCode()==student1.hashCode());
这两个对象的属性值一样,但是hash不一样


因此我们需要重写hashcode方法,让这两个属性值完全一样的对象hash值也一样。
- @Override
- public int hashCode() {
- return Objects.hash(name, age);
- }
我们再打印一下结果:

六.接口和抽象类的区别
(1)接口里的方法都是抽象方法,而抽象类里的方法可以包含普通方法
(2)接口用interface表示,而抽象类用abstract表示
(3)抽象类里可以含有变量,而接口里只能存在常量
(4)抽象类里的抽象方法控制符不能是private,其他的都可以,但是接口里的抽象方法控制符默认是public abstract
(5)接口里不能含有构造方法,而抽象类里可以
(6)一个类可以实现多个接口,但是只能继承一个抽象类
(7)抽象类的设计目的是代码的复用,而接口的核心是定义行为,即类实现接口可以做什么