对象与类(特点)
子类继承性(super关键字(super 关键字主要存在于子类方法中,用于指向子类对象中的父类对象;可以访问父类的属性、函数以及构造函数。)[
在 Java 里面,对于 super 关键字的用法:
-
子父类存在着同名的成员(包括变量和方法)时,在子类中默认是访问子类的成员,可以通过 super 关键字指定访问父类的成员;
-
创建子类对象时,默认会先调用父类无参的构造方法,可以通过 super 关键字指定调用父类的构造方法。
- 如果在子类的构造方法上没有指定调用父类的构造方法,那么 Java 编译器会在子类的构造方法内加上 super() 语句;
- super 关键字调用父类的构造函数时,该语句必须要是子类构造函数中的第一个语句;
- super 与 this 关键字不能同时出现在同一个构造函数中调用其他的构造函数。因为两个语句都需要是第一个语句。
])
系统自定义类(Object类)
成员变量隐藏
方法重载(构造方法重载、静态方法重载)
方法重写(在子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的内容不同,以实现不同于父类的功能,这种方式被称为方法重写(override),又称为方法覆盖。)重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。当父类中的方法无法满足子类需求或子类具有特有功能的时候,需要方法重写。[
在重写方法时,需要遵循下面的规则:
-
参数列表必须完全与被重写的方法参数列表相同;
-
返回的类型必须与被重写的方法的返回类型相同(Java1.5 版本之前返回值类型必须一样,之后的 Java 版本放宽了限制,返回值类型必须小于或者等于父类方法的返回值类型);
-
访问权限不能比父类中被重写方法的访问权限更低(public>protected>default>private);
-
重写方法一定不能抛出新的检査异常或者比被重写方法声明更加宽泛的检査型异常。例如,父类的一个方法声明了检査异常 IOException,在重写这个方法时就不能抛出 Exception,只能拋出 IOException 的子类异常,可以抛出非检査异常。
另外还要注意以下几条:
-
重写的方法可以使用 @Override 注解来标识;
-
父类的成员方法只能被它的子类重写;
-
声明为 final 的方法不能被重写;
-
声明为 static 的方法不能被重写,但是能够再次声明;
-
构造方法不能被重写;
-
子类和父类在同一个包中时,子类可以重写父类的所有方法,除了声明为 private 和 final 的方法;
-
子类和父类不在同一个包中时,子类只能重写父类的声明为 public 和 protected 的非 final 方法;
-
如果不能继承一个方法,则不能重写这个方法。
]
final关键字[
final 关键字的使用
在 Java 中声明类、属性和方法时,可使用关键字 final 来修饰。
-
final 标记的类不能被继承;
-
final 标记的方法不能被子类复写;
-
final 标记的变量(成员变量或局部变量)即为常量,只能赋值一次。
final 关键字修饰类、成员变量和成员方法
- final 修饰类;
final 用来修饰一个类,意味着该类成为不能被继承的最终类。出于安全性的原因和效率上的考虑,有时候需要防止一个类被继承。例如,Java 类库中的 String类,它对编译器和解释器的正常运行有着很重要的作用,不能轻易改变它,因此把它修饰为 final 类,使它不能被继承,这就保证了 String 类的惟一性。同时,如果你认为一个类的定义己经很完美,不需要再生成它的子类,这时也应把它修饰为 final 类。 定义一个 final 类的格式如下:
final class ClassName{
...
}
注意:声明为 final 的类隐含地声明了该类的所有方法为 final 方法。
下面是一个 final 类的例子:
final class A{
...
}
class B extends A{// 错误!不能继承A
...
}
- final 修饰成员变量;
变量被声明为 final 后,成为常值变量(即常量),一旦被通过某种方式初始化或赋值,即不能再被修改。通常 static 与 final 一起使用来指定一个类常量。例如:
static final int SUNDAY=0;
final 变量一般用大写字母和下划线来表示,这是一种编码规定。
- final 修饰成员方法;
用 final 修饰的方法为最终方法,不能再被子类重写,可以被重载。 尽管方法重写是 Java 非常有力的特征,但有时却需要避免这种情况的发生。为了不允许一个方法被重写,在方法的声明中指定 final 属性即可。
]
对象类型转换[
Java 语言允许某个类型的引用变量引用子类的实例,而且可以对这个引用变量进行类型转换。如果把引用类型转换为子类类型,则称为向下转型;如果把引用类型转换为父类类型,则称为向上转型。注意:两者必须为父子关系。
向上转型
把子类对象赋值给父类类型的变量(隐式转换,不用进行强制类型转换),被称为向上转型。
本质:父类的引用指向了子类的对象。
语法:
父类类型 引用名 = new 子类类型();
例如:狗是动物的一种,那么也可以将狗看成一个动物对象。
class Animal{
public void info(){
System.out.println("我是动物");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗在吃东西");
}
public static void main(String[] args) {
Animal animal = new Dog(); // 向上转型
animal.info();
}
}
执行结果:
我是动物
狗对象可以调用该方法,那么同样猫也可以调用该方法,这就做到了在父类中定义一个方法可以完成各个子类的功能。由于向上转型是一个从较具体类到较抽象类的转换,所以它总是安全的,因为我们可以说狗是动物,但是不能说动物是狗。
向上转型的特点如下:
-
向上转型对象不能操作子类新增的成员变量(失掉了这部分属性),不能使用子类新增的方法(失掉了一些功能);
-
向上转型对象可以操作子类继承或重写的成员变量,也可以使用子类继承的或重写的方法;
-
如果子类重写了父类的某个方法后,当对象的向上转型对象调用这个方法时一定是调用了这个重写的方法,因为程序在运行时知道,这个向上转型对象的实体是子类创建的,只不过损失了一些功能而已。
向下转型
向下转型是指子类引用父类对象,就是将父类对象能转换成子类对象,这时需要满足两个条件:一是必须执行强制类型转换;二是必须确保父类对象是子类的一个实例,否则抛出异常。
语法:
子类类型 引用名 = (子类类型)父类引用;
class Animal{
public void info(){
System.out.println("我是动物");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗在吃东西");
}
public static void main(String[] args) {
Animal animal = new Dog(); // 向上转型
Dog animal1 = (Dog) animal; // 向下转型
animal1.eat();
}
}
执行结果:
狗在吃东西
向下转型的特点如下:
-
向下转型对象可以操作父类及子类成员变量和成员方法;
-
向下转型对象访问重写父类的方法时,操作的是子类的方法;
-
向下转型必须进行强制类型转换;
-
向下转型必须保证父类对象引用的是该子类的对象,如果引用的是父类的其他子类对象,会抛出类型不匹配异常。
]
抽象类(在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,那么这样的类称为抽象类。)[
在 Java 中抽象类的语法格式如下:
<abstract>class<class_name> {
<abstract><type><method_name>(parameter-list);
}
其中,abstract 表示该类或该方法是抽象的;class_name 表示抽象类的名称;method_name 表示抽象方法名称,parameter-list 表示方法参数列表。
例子:
抽象方法的 3 个特征如下:
注意:在使用 abstract 关键字修饰抽象方法时不能使用 private 修饰,因为抽象方法必须被子类重写,而如果使用了 private 声明,则子类是无法重写的。
抽象类的定义和使用规则如下:
我们来看一个例子:
我们先定义一个抽象的动物类,抽象类作为一个父类,可以定义抽象方法,也可以定义一般方法。
再定义一个狗类,子类 Dog 继承了抽象类,那么就必须将其抽象方法实现。
执行结果:
// 这是一个抽象类
public abstract class Test {
}
如果一个方法使用 abstract 来修饰,则说明该方法是抽象方法,抽象方法只有声明没有实现。需要注意的是 abstract 关键字只能用于普通方法,不能用于 static 方法或者构造方法中。
注意抽象方法是没有方法体的(也就是方法后面是没有大括号的)。凡是继承这个抽象类的实体子类,都必须要实现这个抽象方法。
public abstract class Test {
public abstract void eat(); // 抽象方法
public void sleep(){
System.out.println("我正在睡觉"); //普通方法
}
}
- 抽象方法没有方法体,必须使用 abstract 关键字来修饰;
- 抽象方法必须存在于抽象类中;
- 子类重写父类时,必须重写父类所有的抽象方法。
- 抽象类和抽象方法都要使用 abstract 关键字声明。
- 如果一个方法被声明为抽象的,那么这个类也必须声明为抽象的。而一个抽象类中,可以有 0n 个抽象方法,以及 0n 个具体方法;
- 抽象类不能实例化,也就是不能使用 new 关键字创建对象,只能被继承;
- 继承一个抽象类,必须重写其抽象方法,否则该类也会被抽象化。
// 抽象类 Animal
public abstract class Animal {
public abstract void eat(); // 抽象方法
public abstract void sleep();// 抽象方法
public void Smile(){
System.out.println("每个动物都会微笑");
}
}
class Dog extends Animal{
// 重写父类的抽象方法 eat()
@Override
public void eat() {
System.out.println("狗爱吃骨头");
}
// 重写父类的抽象方法 sleeep()
@Override
public void sleep() {
System.out.println("小狗要睡觉");
}
public static void main(String[] args){
Dog dog=new Dog(); // 实例化一个Dog对象
dog.eat();
dog.sleep();
dog.Smile();
}
}
狗爱吃骨头
小狗要睡觉
每个动物都会微笑
]
接口[
Java 接口(Interface),是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
它是抽象类的延伸,可以看做是纯粹的抽象类,它的所有方法都没有执行体,是由全局常量和公共的抽象方法所组成。也是解决 Java 无法使用多继承的一种手段。
接口的定义
Java 接口的定义方式与类基本相同,当声明一个接口时,我们使用 interface 这个关键字,在接口中的所有方法都必须只声明方法标识,而不要去声明具体的方法体,因为具体的方法体的实现是由继承该接口的类来去实现的,因此,接口并不用管具体的实现。接口中的属性默认为 public static Final,一个类实现这个接口必须实现这个接口中定义的所有的抽象方法。
接口定义的语法格式如下:
[public] interface interface_name [extends interface1_name[, interface2_name,…]] {
// 接口体,其中可以包含定义常量和声明方法
[public] [static] [final] type constant_name = value; // 定义常量
[public] [abstract] returnType method_name(parameter_list); // 声明方法
}
- interface_name 表示接口的名称;
- extends 表示接口的继承关系;
- interface1_name 表示要继承的接口名称;
- constant_name 表示变量名称,一般是 static 和 final 型的;
- returnType 表示方法的返回值类型;
- parameter_list 表示参数列表,在接口中的方法是没有方法体的。
注意:一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。
例子:
public interface Test {
// 在接口中定义的任何字段都被 static 和 final 修饰
final int a = 10;
// 方法必须被定义为 public 和 abstract 形式,即使不声明,也将隐式地声明为公有的(public)和抽象的(abstract)。
void display();
}
一个接口不能够实现另一个接口,但它可以继承多个其他接口。子接口可以对父接口的方法和常量进行重写。
实现接口
接口的主要用途就是被实现类实现,一个类可以实现一个或多个接口,继承使用 extends 关键字,实现则使用 implements 关键字。因为一个类可以实现多个接口,这也是 Java 为单继承灵活性不足所作的补充。类实现接口的语法格式如下:
<public> class <class_name> [extends superclass_name] [implements interface1_name[, interface2_name…]] {
// 主体
}
- superclass_name:需要继承的父类名称;
- interface1_name:要实现的接口名称。
例子:
// 定义一个动物接口
interface Animal {
public static final int num = 5;
public abstract void eat();
}
// 定义一个猫类,实现动物接口
class Cat implements Animal {
@Override
public void eat() {
System.out.println("猫吃东西");
}
}
public class Test{
public static void main(String[] args) {
Cat cat = new Cat(); // 实例化一个猫对象
cat.eat();
}
}
执行结果:
猫吃东西
实现接口需要注意以下几点:
-
实现接口与继承父类相似,一样可以获得所实现接口里定义的常量和方法。如果一个类需要实现多个接口,则多个接口之间以逗号分隔;
-
一个类可以继承一个父类,并同时实现多个接口,implements 部分必须放在 extends 部分之后;
-
一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法);否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。
接口的特点
-
Java 接口中的成员变量默认都是 public,static,final 类型的(都可省略),必须被显示初始化,即接口中的成员变量为常量;
-
Java 接口中的方法默认都是 public,abstract 类型的(都可省略),没有方法体,不能被实例化;
-
Java 接口中只能包含 public,static,final 类型的成员变量和 public,abstract 类型的成员方法;
-
接口中没有构造方法,不能被实例化;
-
一个接口不能实现(implements)另一个接口,但它可以继承多个其它的接口;
-
Java 接口必须通过类来实现它的抽象方法;
-
当类实现了某个 Java 接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类;
-
不允许创建接口的实例(实例化),但允许定义接口类型的引用变量,该引用变量引用实现了这个接口的类的实例;
-
一个类只能继承一个直接的父类,但可以实现多个接口,间接的实现了多继承。
Java 中接口和继承的区别:
-
不同的修饰符修饰(接口:interface),(继承:extends);
-
在面向对象编程中可以有多继承!但是只支持接口的多继承,不支持‘继承’的多继承;
-
在接口中只能定义全局常量,和抽象方法,而在继承中可以定义属性方法,变量,常量等;
-
某个接口被类实现时,在类中一定要实现接口中的抽象方法,而继承想调用哪个方法就调用哪个方法。
]