学习目录
抽象类
抽象类的概念
在面对对象的概念中,所以的对象都是通过类来描述的,而普通的类是一个完善的功能类,可以直接实例化对象
并且在类中可以包含常量、成员变量(属性)、成员方法、构造方法等内容
抽象类和普通类不一样的是,抽象类当中可以包含抽象方法
抽象方法是指没有具体实现方法的内容,同时抽象方法必须使用abstract
关键字来修饰
例如:
抽象类的语法形式:
一个类如果被 abstract
修饰就被称为抽象类
抽象类中使用 abstract
修饰的方法称为抽象方法,抽象方法可以不用给出具体的实现
// 抽象类必须使用 abstract 关键字来修饰
abstract class A{
// 抽象类也是类,里面可以包含普通方法、属性、构造方法
int a;
//普通的方法
public void fun(){
System.out.println("方法具体实现的内容");
}
//抽象方法 没有具体实现的内容 并且要用 abstract 来修饰
public abstract void fun1();
}
抽象类的使用限制
- 抽象类不能实例化对象
例如:把上面创建的抽象类进行实例化
public static void main(String[] args) {
A a = new A();
}
结果:
由于A是抽象类,所以不能实例化,编译器会报错
- 抽象方法里的成员方法不能是
private
的
注意:是抽象方法
不能是 private(私有)
的权限
抽象方法没有加访问限定符时,默认是public
- 抽象方法不能被
final
和static
修饰,因为抽象方法要被子类重写
- 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用
abstract
修饰
```java
//定义的 抽象类
abstract class A{
// 构造方法,用来显示执行的顺序
public A(){
System.out.println("正在执行A类的构造方法");
}
// 定义的 抽象方法
public abstract void fun();
}
class B extends A{
// 构造方法,用来显示执行的顺序
public B(){
System.out.println("正在执行B类的构造方法");
}
@Override
public void fun() { //必须重写抽象类的方法
System.out.println("Hello China!");
}
}
public class Test {
public static void main(String[] args) {
A a = new B(); // 向上转型
}
}
- 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定是抽象类
练习
使用抽象类来实现一个程序,可以实现三种不同事物的行
三种的事物为:
孩子:吃饭、去学校学习知识、睡觉
父亲:吃饭、上班工作、睡觉
母亲:吃饭、做家务、睡觉
- 定义一个
抽象类
功能和父类差不多,是对共性的抽取
不过抽象类可以定义抽象方法,可以不用写方法具体实现过程
// 抽象类
abstract class Action{
// 抽象方法
public abstract void eat(); // 吃饭
public abstract void sleep(); // 睡觉
public abstract void action(); //行为
}
- 定义 子类(孩子)
// 定义一个孩子的类
class Child extends Action{
// 他的行为
@Override
public void eat() {
System.out.println("孩子:吃饭!");
}
@Override
public void action() {
System.out.println("孩子:去学校学习知识!");
}
@Override
public void sleep() {
System.out.println("孩子:睡觉!");
}
}
- 定义 子类(父亲)
// 定义一个 父亲 的类
class Dod extends Action{
// 他的行为
@Override
public void eat() {
System.out.println("父亲:吃饭!");
}
@Override
public void action() {
System.out.println("父亲:上班工作!");
}
@Override
public void sleep() {
System.out.println("父亲:睡觉!");
}
}
- 定义 子类(母亲)
// 定义一个 母亲 的类
class Mom extends Action{
// 他的行为
@Override
public void eat() {
System.out.println("母亲:吃饭!");
}
@Override
public void action() {
System.out.println("母亲:做家务!");
}
@Override
public void sleep() {
System.out.println("母亲:睡觉!");
}
}
- 引用抽象类来调用子类
public static void main(String[] args) {
//通过静态方法来创建对象
fun(new Child());
fun(new Dod());
fun(new Mom());
}
public static void fun(Action a){
a.eat();
a.action();
a.sleep();
}
// 或者可以写成,不过下面的方法比较繁琐,需要一个一个引用,推荐写上面
public static void main(String[] args) {
Action a = new Dod();
a.eat();
a.sleep();
a.action();
Action b = new Mom();
........
Action c = new Child();
........
}
接口
接口的概念
接口可以理解成一个特殊的类,用来解决Java不能多继承的一种方法
接口里面全部是由全局常量
和抽象方法
所组成
接口在日常使用的过程中是用来制定规范标准,在使用时,只要符合规范标准,就可以通用
在Java中,接口可以看成是:一种行为的规范(标准),是一种引用数据类型
接口语法规则
定义接口需要使用 interface
关键字,其语法的形式和 类的定义差不多
在接口里不能有非抽象方法,不能有具体的实现,如果非要写进去,那么可以在方法的最前面加上 defint
,加上后就可以有具体的实现
public interface 接口名称{
public abstract void func();
// 在接口里所有的方法是默认为 公开的抽象类
// public abstract 是默认的,可以不写
//例如:
void func();
}
默认规范:
在创建接口时, 接口的命名一般以大写字母 I
开头
阿里编码规范中约定, 接口中的方法
和属性
不要加任何修饰符号
, 保持代码的简洁性
接口的使用
接口是不能直接使用的,必须要有一个 类
来实现
接口,在类中实现接口的所以抽象方法
子类和父类是使用 extends
来继承关系,类和接口是使用 implements
实现关系
接口的语法形式:
class 类的名称 implements 接口名称{
..........
}
例如:
// 定义一个接口
interface IShape{
// 抽象方法
void func();
default void func2(){
System.out.println("在接口使用非抽象类!必须使用 defaule 修饰");
}
public static void func3(){
System.out.println("接口里可以有静态方法!");
}
}
// A 类实现接口 IShape 必须要重写接口里所以的抽象方法
class A implements IShape{
@Override // 用来判断名字是否有写错
// 重写接口里抽象方法的方法
public void func() {
System.out.println("接口的抽象方法必须重写!");
}
@Override
public void func2() {
System.out.println("default 写不写是看情况,需要就写!");
}
// static 修饰的方法不可以重写
}
接下用接口来实现多态
例如:
// 定义一个接口,使用关键字 interface
interface IShape{
// 定义一个抽象方法!
void draw();
}
// 使用类来实现接口的所以方法
class A implements IShape{
// 快速的写抽象类的方法快捷键: CTRL + i(鼠标要点到类名那里,在按快捷键)
@Override // 用来判断名字是否有写错
// 重写接口里抽象方法的方法
public void draw() {
System.out.println("用接口来实现的矩形!");
}
}
// 定义一个 花 类来实现接口的方法
class Flower implements IShape{
@Override
public void draw() {
System.out.println("花❀!");
}
}
public class Test {
// 这是用来调用接口的方法
public static void func(IShape a){
a.draw();
}
public static void main(String[] args) {
// 这是向上转型来实现多态
func(new A());
func(new Flower());
//******方法二*********
IShape b = new A();
func(b);
IShape c = new Flower();
func(c);
}
}
实现多个接口
在Java中,类和类之间是不能多继承的,一个类只能继承一个类
但是一个类可以实现多个接口
实现多个接口是使用逗号
IDEA 中使用 ctrl + i 快速实现接口
例如:
定义多个接口
// 接口
interface IA{
void Flying();//飞行
}
interface IB{
void swimming();//游泳
}
interface IC{
void run();//奔跑
}
定义一个父类
// 父类
class Animal {
// 成员变量
public String name;
int age;
// 构造方法
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
// 方法
public void eat(){
System.out.println("吃饭!");
}
}
定义一个子类来继承父类,来实现多接口
// 子类继承父类,再实现接口
class Dog extends Animal implements IC,IB{
//构造方法
public Dog(String name, int age) {
// 初始化父类的成员变量
super(name, age);
}
// 父类调用子类方法
public void eat(){
System.out.println(name + "在吃饭!");
}
// 重写接口的抽象方法
@Override
public void swimming() {
System.out.println(name + "在游泳");
}
@Override
public void run() {
System.out.println(name + "在奔跑");
}
}
最后测试:
public class Test {
public static void walk(IC a){
a.run();
}
public static void func(Animal a){
a.eat();
}
public static void main(String[] args) {
// 接口的使用,实现多态
walk(new Dog("小黑",8));
//父类调用子类来实现多态
func(new Dog("花花",6));
}
}
结果:
接口间的继承
在Java中,接口和接口之间是可以多继承的
接口可以继承多个接口, 达到复用的效果. 使用extends
关键字
interface A{
void func1();
}
interface B{
void func2();
}
// 接口和接口之间的继承
interface C extends A,B{
void func3();
}
// 类继承接口
class D implements C{
@Override
public void func1() {
}
@Override
public void func2() {
}
@Override
public void func3() {
}
}
- 如果一个接口里继承了多个接口,那么如果一个类继承了这个接口,就要重写继承接口的所以方法
以上例子中,C 接口继承了 A 和 B接口
相当于把多个接口合并在一起了,那么D类 继承 C接口就要重写 3 个方法
- 接口和接口使用的是
extends
类和接口使用的是implements
接口使用实例
例如:
给对象的数组进行排序
class Student {
public String name;
public int age;
// 构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "A{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test2 {
public static void main(String[] args) {
Student[] s = new Student[3];
s[0] = new Student("小明",18);
s[1] = new Student("小红",16);
s[2] = new Student("小刚",23);
Arrays.sort(s);
System.out.println(Arrays.toString(s));
}
}
结果:运行出错
以上出错的原因是没有指定根据什么进行比较,是 姓名 还是 年龄
所以需要自定义比较的内容,需要重新一个 CompartTo
方法
import java.util.Arrays;
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "A{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
if(this.age - o.age > 0){
return 1;
} else if (this.age - o.age < 0) {
return -1;
}else {
return 0;
}
}
}
public class Test2 {
public static void main(String[] args) {
Student[] s = new Student[3];
s[0] = new Student("小明",18);
s[1] = new Student("小红",16);
s[2] = new Student("小刚",23);
Arrays.sort(s);
System.out.println(Arrays.toString(s));
}
}
结果:
在 sort 方法中会自动调用 compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象.
然后比较当前对象和参数对象的大小关系(按年龄来算).
如果当前对象应排在参数对象之前, 返回小于 0 的数字;
如果当前对象应排在参数对象之后, 返回大于 0 的数字;
如果当前对象和参数对象不分先后, 返回 0;
再次执行程序, 结果就符合预期了
对于 sort 方法来说, 需要传入的数组的每个对象都是 "可比较"
的, 需要具备compareTo
这样的能力. 通
过重写 compareTo 方法
的方式, 就可以定义比较规则
下面是为了进一步加深对接口的理解,尝试自己实现一个 sort 方法来完成刚才的排序过程(使用冒泡排序)
public static void bubbleSort(Comparable[] array){
for (int i = 0; i < array.length-1 ;i++) {
for (int j = 0; j < array.length-1-i; j++) {
// compareTo 是比较二个变量的大小
if(array[j].compareTo(array[j+1]) > 0){
Comparable tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
}
public static void main1(String[] args) {
Student[] s = new Student[3];
s[0] = new Student("小明",18);
s[1] = new Student("小红",16);
s[2] = new Student("小刚",23);
bubbleSort(s);
//Arrays.sort(s);
System.out.println(Arrays.toString(s));
}
}
抽象类和接口的区别
1.实现方法
抽象类:抽象类里可以有具体实现的方法,和被 abstract(抽象方法)
,因为存在抽象方法,所以该类是抽象类
接口:接口里只能有有抽象方法,不能有方法的具体实现
2.子类使用的关键字的不同
抽象类:定义是使用abstract
,子类继承抽象类是使用 extends 关键字
,如果子类不是抽象类,则需要重写抽象类的具体实现方法,其他看情况可写可不写
接口:定义是使用 intesface
,子类是通过 implements 关键字
来实现,需要实现接口里所以的方法
3.权限的区别
抽象类:可以使用public、protected和default
修饰符
接口:只能使用 public
修饰符
4.关系的不同
抽象类:一个抽象类可以实现多个接口
接口:接口是不能继承抽象类的,但是接口可以使用 extends
关键字类继承多个父类接口
5.子类继承的限制
抽象类:一个子类只能继承一个抽象类
接口:一个子类可以实现多个接口
Object 类
Object
是由 Java默认提供的一个类,Object
是所有类的父类
,即所有类
可以使用 Object
类来引用
例如:
可以直接使用 Object 来引用类
class A{
}
class B{
}
public class Test2 {
public static void func(Object obj){
}
public static void main(String[] args) {
func(new A());
func(new B());
}
}
Object类
是参数的最高统一类型
,但是Object类也存在有定义好的一些方法。如下:
对象比较equals方法
在Java中,进行 ==
比较时,
1.如果==
左右两侧是基本类型变量,比较的是变量中值是否相同
2.如果==
左右两侧是引用类型变量,比较的是引用变量地址是否相同
3.如果要比较对象中内容,必须重写
Object 中的equals方法
,因为equals方法默认也是按照地址比较
如果想比较内容就要重写一个 equals
class A{
private String name;
private int age;
// 构造方法,初始化成员变量
public A(String name,int age){
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
// 判断 obj 是否是空类型
if(obj == null){
return false;
}
//判断是二个是否相等
if (this == obj){
return true;
}
// 判断是否是同一类型
if (!(obj instanceof A)){
return false;
}
// 强制类型转换
A s = (A)obj;
// 判断是否相等,重写的是判断名字是否相等
if(this.name.equals(s.name) && this.age == s.age){
return true;
}
return false;
}
}
class B{
}
public class Test {
public static void main(String[] args) {
A a = new A("小黑",8);
A b = new A("小黑",8);
System.out.println(a.equals(b));
}
}
如果比较内容是否相等时,一定要重写 equals
方法
hashcode 方法
hashcode 方法是帮忙算出一个具体对象的位置
不重写的情况下,默认两个对象的hash值不一样
像重写equals方法一样,我们也可以重写hashcode()方法。此时我们再来看看
结果:
上面的哈希值是一样的
hashcode方法
是用来确定对象在内存中存储的位置是否相同- 事实上hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的
散列码,进而确定该对象在散列表中的位置
总结
抽象类
- 抽象类和普通的类的区别在于抽象类里有
抽象方法
,如果除开抽象类里的抽象方法,那么和普通类没什么区别
抽象类可以没有抽象方法,但是有抽象方法的必须是抽象类
- 一个类如果被
abstract
修饰就被称为抽象类
抽象类中使用abstract
修饰的方法称为抽象方法
,抽象方法可以不用给出具体的实现 - 抽象方法是
不能实例化对象
抽象方法里的成员方法不能是private(私有的)
的
抽象方法不能被final
和static
修饰,因为抽象方法要被子类重写
抽象方法必须被子类继承,继承的子类必须重写父类的抽象方法
抽象类中可以有构造方法
,供子类创建对象时,初始化父类的成员变量
接口
- 接口里面全部是由
全局常量
和抽象方法
所组成,使用接口需要使用interface
来修饰接口
在创建接口时, 接口的命名一般以大写字母I
开头 - 接口当中的成员方法不能有具体的实现
在JDK 1.8 开始,允许接口里有非抽象方法,但是这个方法要用defaule
所修饰 - 接口里可以有
静态方法
- 接口中的
成员变量
默认是public static final
所修饰
成员方法
默认是public abstract
所修饰,都可以不用写,编译器是默认填写了
所以接口中的方法
和属性
不要加任何修饰符号
, 保持代码的简洁性
- 接口是不能实例化的,类和接口之间使用
implements
来实现多个接口 - 子类必须重写抽象方法,而且要加上
public
接口和接口是可以进行多继承的
,使用extends
关键字 - 接口中是不能有
静态代码
和构造方法
- 接口虽然不是类,但是在编译时也会生成一个
字节码文件
,后缀也是.class
- 在定义一个类来实现接口,如果不想实现接口里的抽象方法,可以把这个类定义为
抽象类(abstract)
但是如果这个类被其他的类 继承,那么必须重写它的抽象方法