Java学习——Java面向对象04(面向对象高级 下)
文章目录
一、抽象类与抽象方法
(1). 抽象类
概念:在Java中具有抽象方法的类称为“抽象类”。
在Java中抽象类和抽象方法的修饰符是 abstract ,抽象类必须使用abstract class 声明一个抽象类中可以没有抽象方法。抽象方法必须写在抽象类或者接口中。
抽象类是一种没有完全实现的类。不能用它实例化任何对象,,它的主要用途是用来描述一些钙奶女性的内容,然后再子类中具体实现这些概念,这样可以提高开发效率,统一用户接口,所所以抽象类更多是作为其他类的父类。
格式:
// 抽象类
abstract class 类名{
}
(2). 抽象方法
概念:只声明而未实现的方法称为抽象方法(未实现指的是:没有“ { } ”方法体 ),抽象方法必须使用 abstract 关键字声明。
格式:
// 抽象类
abstract class 类名{
public abstract void 方法名() ; // 抽象方法,只声明而未实现
}
注意事项:
-
抽象类本身是不能直接进行实例化操作的,即:不能直接使用关键字new完成。
-
一个抽象类必须被子类所继承,被继承的子类(如果不是抽象类)则必须覆写(重写)抽象类中的全部抽象方法。如果一个抽象类没有被继承,那么这个抽象类被定义出来就没有意义。
-
抽象类不能使用 final 声明,因为 final 属修饰的类是不能有子类的 , 而抽象类必须有子类才有意义,所以不能。
-
抽象类是能有构造方法的,而且子类对象实例化的时候的流程与普通类的继承是一样的,都是要先调用父类中的构造方法(默认是无参的),之后再调用子类自己的构造方法。
抽象类和普通类的区别:
- 抽象类必须用 public 或 protected 修饰(如果为private修饰,那么子类则无法继承,也就无法实现其抽象方法)。 默认缺省为 public 。
- 抽象类不可以使用 new 关键字创建对象, 但是在子类创建对象时, 抽象父类也会被 JVM 实例化。
- 如果一个子类继承抽象类,那么必须实现其所有的抽象方法。如果有未实现的抽象方法,那么子类也必须定义为 abstract类 。
举例:
//抽象类Fruit
public abstract class Fruit {
public void say() {
System.out.println("This is fruit!");
}
//抽象方法 abstract修饰
public abstract void kinds();
}
创建一个Apple类继承它
//Apple类
public class Apple extends Fruit{
public static void main(String[] args) {
// f变量是父类类型,指向子类实例,发生多态
Fruit f = new Apple();
f.say();
f.kinds();
}
//父类抽象方法的重写
@Override
public void kinds() {
System.out.println("And I'm a Apple!");
}
}
运行结果:
继承抽象类会报错,显示需要添加未实现的抽象方法
二、接口
概念:
比抽象类更加抽象的是接口,在接口中所有的方法都是抽象的,全部属性都是全局常量。
(1). 接口定义
格式:
interface 接口名称{
全局常量 ;
抽象方法 ;
}
(2). 接口实现
由接口生成子类不是通过 extends 实现的,而是使用关键字 implements 来实现一个接口。
接口可以多实现,接口名之间使用逗号隔开。
格式:
class 子类 implements 父接口1,父接口2...
{
}
当一个类即要实现接口,又要继承抽象类的话,按照以下格式实现:
class 子类 extends 父类 implements 父接口1,父接口2...{
}
接口因为都是抽象部分,不存在具体的实现,所以允许多继承,格式如下:
interface C extends A,B{
}
如果一个接口要想使用,必须依靠子类。 子类(如果不是抽象类的话)要实现接口中的所有抽象方法。
接口和抽象类的区别:
- 抽象类要被子类继承,接口要被类实现。
- 接口只能声明抽象方法,抽象类中可以声明抽象方法,也可以写非抽象方法。
- 接口里定义的变量只能是公共的静态的常量,抽象类中的变量可以是普通变量(接口中的变量定义默认为公共静态)。
- 抽象类使用继承来使用, 无法多继承。 接口使用实现来使用, 可以多实现
- 抽象类中可以包含static方法 ,但是接口中不允许(静态方法不能被子类重写,因此接口中不能声明静态方法)
- 接口不能有构造方法,但是抽象类可以有
举例:
Food 接口类:
public interface Food {
//可以看到定义的变量 a 与 num 均为加粗斜体显示,表示a 与num 均为公共静态常量
//不过num定义出来,前面的修饰符系统是默认给出的
public static final int a = 10;
int num = 10;
void say();
}
Pear类实现接口:
public class Pear implements Food{
public static void main(String[] args) {
Food f = new Pear();
f.say();
System.out.println(f.num);
}
@Override
public void say() {
System.out.println("I'm a Pear!");
}
}
运行结果:
三、Object类
概念:
Object类是所有类的父类(基类),如果一个类没有明确的继承某一个具体的类,则将默认继承Object类。
例如,我们定义一个类:
public class Person{ }
其实它被使用时,是这样的:
public class Person extends Object{ }
使用Object可以接收任意的引用数据类型
比如任意一种数据类型的数据传入,我们这时可以用 Object 来接收
接下来了解一下Object类中的 toString 与 equals 方法。
(1). toString
建议重写Object中的toString方法。
此方法的作用:返回 对象 的字符串表示形式。
Object的 toString 方法, 返回对象的内存地址。
举例:
首先先创建一个具构造方法,具set/get方法普通的Person类:
public class Person {
public String name;
public int age;
//构造方法
public Person(){
}
public Person(String name,int age) {
System.out.println("Person类被创建!");
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 void show() {
System.out.println("这是个人,姓名:" + name + ",年龄为:" + age);
}
}
创建一个Demo1 测试:
public class Demo1 {
public static void main(String[] args) {
Person p1 =new Person();
Person p2 =new Person();
System.out.println(p1.toString());
}
}
运行结果:
我们跳到toString方法界面,可以看到toString方法输出内容:
输出内容的第一项是 该对象对应的那个类所在地址:test.Person
我们便是建议取重写toString方法:
在Person类中重写出 我们想要在控制台输出的内容。
在Person类添加一下代码:
public String toString() {
return "This is a Person! ,姓名为:" + this.name + ",年龄为:" + this.age;
}
返回Demo1,运行结果:
重写toString方法是为了调用该方法,输出我们想要的结果。
(2). equals
建议重写Object中的equals(Object obj)方法。
此方法的作用:指示某个其他对象是否“等于”此对象。
Object的 equals 方法:实现了对象上最 具区别的可能等价关系 ;
也就是说,对于任何非空引用值x和y ,当且仅当 x和y引用同一对象( x == y具有值true )时,此方法返回true 。
举例,沿用上面的Person类,我们将Demo1修改如下:
public class Demo1 {
public static void main(String[] args) {
Person p1 =new Person();
Person p2 =new Person();
p1.setName("王二");
p1.setAge(18);
p1.setName("王二");
p1.setAge(18);
System.out.println(p1.equals(p2));
}
}
运行结果:
我们跳到 equals 方法,该方法中的 == 就是比较两个对象的内存地址是否相同。
而我们调用 equals 方法是想要比较两个对象中某些属性是否一致,如果一致,我们便认为两个对象相同。
在Person类中,添加equals方法的重写:
public boolean equals(Object obj) {
if(this == obj) { //如果这两个对象都相同了,那必然一样
return true;
}
if(obj == null) { //比较对象为空时
return false;
}
if(obj instanceof Person) { //如果比较对象是Person类
Person p = (Person) obj; //将比较对象转换为Person类
if(this.name.equals(p.name) && this.age == p.age) {
return true;
}
}
return false; //均不成立
}
此时再次运行Demo1时,得到我们想要的比较结果(当两个对象的姓名和年龄相等时,我们认为两对象相同):
equals方法重写时的五个特性:
- 自反性 :对于任何非空的参考值x , x.equals(x)应该返回true 。
- 对称性 :对于任何非空引用值x和y , x.equals(y)应该返回true当且仅当y.equals(x)回报true 。
- 传递性 :对于任何非空引用值x , y和z ,如果x.equals(y)回报true个y.equals(z)回报true ,然后
x.equals(z)应该返回true 。 - 一致性 :对于任何非空引用值x和y ,多次调用x.equals(y)始终返回true或始终返回false ,前提是未修改对象
上的equals比较中使用的信息。 - 非空性 :对于任何非空的参考值x , x.equals(null)应该返回false 。
对于上面重写 toString 与 equals 方法,eclipse给我们有提供快速生成方法:
使用eclipse快捷键 shift + alt + s ,跳出一下界面:
点击要重写的equals 或者 toString 方法 , 勾选所想要比较的属性:
系统生成与我们自己重写的相比较(被注释的为我们重写的):
生成 toString 方法与上同