Java
中提供了abstract
这个关键字用于创建抽象方法和抽象类。抽象方法书写格式:public abstract void draw(); |
---|
abstract
这个修饰符修饰这个类,这个类叫做抽象类。private
,final
,static
修饰package demo4;
abstract class Shape{
// public static int count = 0 ;
// public int size = 0;
// public void test(){
// }
public abstract void draw();
}
abstract class A extends Shape{
public abstract void testA();
}
class B extends A{
@Override
public void testA() {
}
@Override
public void draw() {
}
}
package demo4;
abstract class Person{
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person{
public Student(){
super("Aileen",21);
}
}
package demo3;
abstract class Shape{
//抽象方法
public abstract void draw();
}
class Cycle extends Shape{
@Override
public void draw() {
System.out.println("○");
}
}
class Rect extends Shape{
@Override
public void draw() {
System.out.println("矩形");
}
}
public class Test {
//定义一个drawing方法,接受Shape类型的参数
public static void drawing(Shape myshape){
//调用传入对象的draw方法
myshape.draw();
}
public static void main(String[] args) {
//向上转型,将Circle对象赋值给Shape类型的引用
Shape shape1 = new Cycle();
//向上转型,将Rect对象赋值给Sgape类型的引用
Shape shape2 = new Rect();
//调用drawing方法传入shape1
drawing(shape1);
//调用drawing方法传入shape2
drawing(shape2);
}
}
抽象类的书写格式:abstract class 类名(); |
---|
抽象类
:一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就叫做抽象类。
package demo4;
abstract class Shape{
// public static int count = 0 ;
// public int size = 0;
// public void test(){
// }
public abstract void draw();
}
class Rect extends Shape{
@Override
public void draw() {
System.out.println("画矩形");
}
}
class Cycle extends Shape{
@Override
public void draw() {
System.out.println("画一个○");
}
}
class Flower extends Shape{
@Override
public void draw() {
System.out.println("❀");
}
}
public class Test {
public static void func(Shape shape){
shape.draw();
}
public static void main(String[] args) {
//Shape shape = new Shape();
//如果上面的func是静态的,那么我们可以在这个静态的main方法里面直接调用func
func(new Cycle());
func(new Flower());
func(new Rect());
//******************************************************************************
//静态方法不能调用非静态,如果非要调用非静态则需要对象的引用去调用。
//如果上面的func是非静态的(static去掉),则可以按照下面的方法去调用func;
Test test = new Test();
test.func(new Flower());
}
}
抽象类的作用:
接口
接口
:多个类的公共规范,是一种引用数据类型,不能直接new
接口对象。
interface
来定义public static final
的,所以可以省略不写,如下面的变量a。public abstract
可以省略不写),即接口中的每一个方法都是public
的抽象方法,会被隐式指为public abstract
,其他修饰符都会报错。default
或static
修饰。如下面的test和func方法的实现。package demo5;
interface IShape{
int a = 10;
void draw();
public default void test(){
System.out.println("cool");
}
public static void func(){
}
}
implement
它的接口,谁就对这个接口的方法进行重写。⚠️但记得一点重写的方法要加public修饰符否则会报错,因为在接口中我们虽然不用加修饰符可以直接写成 void 方法名(),那是因为编译器已经隐式给它加了public abstract
,只是没展示而已。如下所示:)
implements
来来关联。(接口里面的所有抽象方法都要被重写)
接口的使用
extends
的关系,类与接口是implements
的实现关系。抽象类的书写格式:public class 类名称 implements 接口名称{ ...... }; |
---|
I
开头。package demo5;
interface IShape{
void draw();
}
class Rect implements IShape{
@Override
public void draw() {
System.out.println("画矩形");
}
}
class Cycle implements IShape{
@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 myShape){
myShape.draw();
}
public static void main(String[] args) {
// IShape iShape = new IShape();
//写法1:
func(new Cycle());
func(new Flower());
func(new Rect());
System.out.println("--------------------------");
//写法2:
IShape iShape1 = new Rect();
IShape iShape2 = new Cycle();
IShape iShape3 = new Flower();
func(iShape1);
func(iShape2);
func(iShape3);
}
}
运行结果:
接口执行类的过程演示
Java
当中不能实现多继承,它只能进行单继承,但它可以通过类去实现多个接口。由于Dog没有实现飞的接口所以不能调用。
package demo2;
public class Roboot implements IRun{
@Override
public void run() {
System.out.println("机器人在用两条腿跑~");
}
}
public class Test {
public static void testRun(IRun iRun){
iRun.run();
}
public static void main(String[] args) {
testRun(new Roboot());
}
接口间的继承
Java
中,类与类之间是单继承的,一个类可以实现多个接口,接口和接口之间可以多继承。在Java
中我们可以通过接口实现多继承。extends
关键字。
抽象类和接口的区别
NO | 区别 | 抽象类(abstract) | 接口(Interface) |
---|---|---|---|
1 | 结构组成 | 普通类+抽象方法 | 抽象方法+全局常量 |
2 | 权限 | 各种权限 | public |
3 | 子类使用 | 使用extends关键字继承抽象类 | 使用implements关键字实现接口 |
4 | 关系 | 一个抽象类可以实现若干个接口 | 接口不能继承抽象类,但是接口可使用extends关键字继承多个父接口 |
5 | 子类限制 | 一个子类只能继承一个抽象类 | 一个子类可以实现多个接口 |
public static final
的,方法只能是public abstract
的。Java
当中不能多继承的特性。Object 类
在Java中Object
类是所有类的父类。默认它们都继承了Object
这个父类,所有类的引用都可以用Object
的引用来进行接收。
通过上面的代码我们可以知道Object
作为所有类的父类,可以发生向上转型。
我们通过ALT+7
可以看到所有Object
的方法,这意味着我们可以在其它类中调用它的这些方法。
Object几个比较重要的方法
1.toString方法的使用
person
这个子类作为对象传给父类Object
的引用x
【向上转型】obj
这个引用引用的对象不一样,其表现的行为就会不一样【多态】)。obj
调用toString
方法(由于子类Person中重写了obj
的toString
方法,则会先调用子类的,否则调用父类的【动态绑定】)2.equals方法的使用
package demo3;
class Person{
public String name ;
public Person(String name){
this.name = name;
}
@Override
public String toString() {
return "name:" + name;
}
}
public class Test {
public static void main(String[] args) {
Person person1 = new Person("张三");
Person person2 = new Person("李四");
System.out.println(person1 == person2);
}
}
运行结果:
根据运行结果我们可以知道person1
和person2
为引用数据类型变量,它们所指向的是各自的地址,所以不能直接比较。
下面是Object类中equals方法的默认实现:
==
比较的效果是一样的。
package demo3;
class Person{
public String name ;
public Person(String name){
this.name = name;
}
@Override
public String toString() {
return "name:" + name;
}
}
public class Test {
public static void main(String[] args) {
Person person1 = new Person("张三");
Person person2 = new Person("张三");
System.out.println(person1.equals(person2));
//因为person里面没有equals方法所以会调用object的equals方法
}
}
运行结果:
我们知道在Java中基本数据类型int
,char
,double
判断是否相等是用==
号,而引用数据类型(String
)是用equals
,但是上面代码中我们比较Person
的引用里面传的参数name是否相等的时候用了equals编译器运行结果却是false
,这是为什么呢?
Pesrson
这个类中并没有equals这个方法,所以它只能调用默认父类类Object
里面的equals
方法,但是在默认类里面的equals
方法是一个假的方法,因为根据上面的学习我们可以知道,这个equals
方法是用来判断这两个值的地址是否相等而非用来判断他们的值是否相等。所以我们需要在子类Person
中将equals
方法进行重写。此外,由于我们要比较的name
这个属性是Person
这个类的它的类型是person
,而equals
方法传递的参数类型是Object
类型的,所以我们需要通过向下转型将Object
类型的对象转为Person
类型,这样才能调用Person
的name
属性去判断这两个值是否相等。package demo3;
class Person{
public String name ;
public Person(String name){
this.name = name;
}
@Override
public String toString() {
return "name:" + name;
}
@Override
public boolean equals(Object obj) {
Person tmp = (Person) obj;
return tmp.name.equals(this.name);
}
}
public class Test {
public static void main(String[] args) {
Person person1 = new Person("张三");
Person person2 = new Person("张三");
System.out.println(person1.equals(person2));
//因为person里面没有equals方法所以会调用object的equals方法
}
}
思考:为啥我们下面的代码无需重写也能比较值?
String str1 = "Aileen";
String str2 = "Aileen";
System.out.println(str1.equals(str2));
在Java
中,String
类是Java
标准库中的类,而上面的Person
类是我们自定义的类。在Java
标准库中的类已经重写了equals
方法,String
作为标准库中的一个类,Java
开发团队已经为它重写了equals
方法,这是因为在字符串的比较中,内容的相等性比引用的相等性更常用。
3.hashcode方法的使用
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
回忆上面的toString
方法的源码里有一个hashCode()方法,如上所示,他帮我们算了一个具体对象的位置,它是一个内存地址,它通过调用Integer.toHexString()方法,将这个地址以16进制输出。
我们通过调用hashCode
可以发现,对于person我们调用的是Object的hashCode,按照常理来说,内容一样指向的内存地址按道理来说也应该一样,但是我们打印出来的结果却是不一样的,所以我们需要在person里面重写hashCode这个方法。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hashCode(name);
}
运行结果:
根据上面的代码,我们通过编译器自动生成重写的equals和hashCode方法,运行以后打印的地址就会变成一摸一样的,这也说明了,我们两个内容相同的值指向的是同一个地址。