21天学会Java之(Java SE第五篇):继承、常用的关键字、封装及权限修饰符、多态
文章目录
面向对象的三个基本特征是:封装、继承、多态
继承
继承可以使我们更容易实现类的扩展,在现实世界中比比皆是。并且继承实现了代码的重用,不用再重新发明*,提高了代码的复用性。
继承是用extends关键字实现的,例如:
class 子类 extends 父类{
Java语句;… … …
}
可以通过一个哺乳动物的例子来理解:
public class ExtendsTest {
public static void main(String[] args) {
Human human=new Human("人类",173);
//拥有自己定义的方法
human.study();
//继承了来自父类的方法
human.suckle();
human.viviparous();
System.out.println("------------------------");
Dog dog=new Dog("狗",50);
dog.bark();
//dog.study();无法使用Human类中的方法
//继承了来自父类的方法
dog.suckle();
dog.viviparous();
}
}
//Mammal是父类,Human和Dog是Mammal的子类
class Mammal{
String name;
int height;
//哺乳动物的特性
public void suckle() {
System.out.println("哺乳");
}
public void viviparous() {
System.out.println("胎生");
}
}
class Human extends Mammal{
//天然拥有父类的属性
public Human(String name,int height){
this.name=name;
this.height=height;
}
//可以重写父类的方法
@Override
public void suckle() {
System.out.println("人类是胎生的");
}
//可以自己定义方法
public void study(){
System.out.println(this.name+"身高"+this.height+",拥有高等智慧。");
}
}
class Dog extends Mammal{
public Dog(String name,int height){
this.name=name;
this.height=height;
}
public void bark(){
System.out.println(this.name+"身高"+this.height+",会汪汪汪。");
}
}
继承的使用要点
- 父类也称作超类、基类、派生类等。
- Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。
- Java中类没有多继承,接口有多继承。
- 子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。
- 如果定义一个类时,没有调用extends,则它的默认继承的父类是:java.lang.Object。
常用的关键字
this关键字
this的本质就是“创建好的对象的地址”,由于在构造方法调用前,对象已经创建。因此,在构造方法中也可以使用this代表“当前对象” 。
this常用的用法:
- 在程序中产生二义性之处,应使用this来指明当前对象;普通方法中,this总是指向调用该方法的对象。构造方法中,this总是指向正要初始化的对象。
- 使用this关键字调用重载的构造方法,避免相同的初始化代码。但只能在构造方法中用,并且必须位于构造方法的第一句。
- 静态方法不需要定义类的对象就可以直接使用的,而this表示调用这个方法的对象,两个同时使用就矛盾了,静态方法被调用的时候是不存在类的对象的。所以,this不能用于static方法中。
static关键字
在类中,用static声明的成员变量为静态成员变量,也称为类变量。 类变量的生命周期和类相同,在整个应用程序执行期间都有效。它有如下特点:
- 为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化。
- 对于该类的所有对象来说,static成员变量只有一份。被该类的所有对象共享。
- 一般用“类名.类属性/方法”来调用。(也可以通过对象引用或类名(不需要实例化)访问静态成员。)
- 在static方法中不可直接访问非static的成员。
核心要点(参考上文this最常的用法的第三点):
- static修饰的成员变量和方法,从属于类。
- 普通变量和方法从属于对象的。
super关键字
-
super是直接父类对象的引用,可以通过super来访问父类中被子类覆盖的方法或属性。
-
使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。
-
若是构造方法的第一行代码没有显式的调用super(…)或者this(…);那么Java默认都会调用super(),含义是调用父类的无参数构造方法,这里的super()可以省略。
final关键字
- 修饰变量:被他修饰的变量不可改变,一旦赋了初值,就不能被重新赋值。
- 修饰方法:该方法不可被子类重写,但是可以被重载(因为重载本质上是两个不同的方法)。
- 修饰类:修饰的类不能被继承。比如:Math、String等。
封装及权限修饰符
封装的作用及其优点
- 封装的作用
- 封装是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
- 封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
- 要访问该类的代码和数据,必须通过严格的接口控制。
- 封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
- 适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
- 编程中封装的具体优点:
- 提高代码的安全性。
- 提高代码的复用性。
- “高内聚”:封装细节,便于修改内部代码,提高可维护性。
- “低耦合”:简化外部调用,便于调用者使用,便于扩展和协作。
权限修饰符
Java中使用权限修饰符来控制哪些细节需要封装,哪些细节需要暴露的。 Java中4种权限修饰符分别为private、default、protected、public,它们说明了面向对象的封装性,所以我们要利用它们尽可能的让访问权限降到最低,从而提高安全性。
权限修饰符的访问范围:
权限修饰符 | 同一个类 | 同一个包 | 子类 | 所有类 |
---|---|---|---|---|
private | √ | |||
无修饰符(default) | √ | √ | ||
protected | √ | √ | √ | √ |
public | √ | √ | √ | √ |
-
私有权限(private):private可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类)。被private修饰的成员,只能在定义它们的类中使用,在其他类中不能调用。
-
默认权限(default):类,数据成员,构造方法,方法成员,都能够使用默认权限,即不写任何关键字。默认权限即同包权限,同包权限的元素只能在定义它们的类中,以及同包的类中被调用。
-
受保护权限(protected):protected可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类)。被protected修饰的成员,能在定义它们的类中,同包的类中被调用。如果有不同包的类想调用它们,那么这个类必须是定义它们的类的子类。
-
公共权限(public):public可以修饰类,数据成员,构造方法,方法成员。被public修饰的成员,可以在任何一个类中被调用,不管同包或不同包,是权限最大的一个修饰符。
封装的使用细节
- 类的属性的处理:
- 一般使用private访问权限。
- 提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意:boolean变量的get方法是is开头)。
- 一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。
- 权限修饰符使用的注意事项
- 并不是每个修饰符都可以修饰类(指外部类),只有public和default可以。
- 所有修饰符都可以修饰数据成员,方法成员,构造方法。
- 为了代码安全起见,修饰符不要尽量使用权限大的,而是适用即可。比如,数据成员,如果没有特殊需要,尽可能用private。
- 修饰符修饰的是“被访问”的权限。
可以用一个人的例子来理解封装的使用:
public class EncapsulationTest {
public static void main(String[] args) {
Person p=new Person("小王",21);
System.out.println(p.getName()); //小王
System.out.println(p.getAge()); //21
System.out.println(p); //Person{name=小王, age=21}
p.setAge(-1); //设置的年龄不合法,赋值失败
System.out.println(p.getAge()); //21
}
}
class Person{
//属性一般使用private修饰
private String name;
private int age;
private boolean flag;
public Person() {
}
public Person(String name, int age) {
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) {
//在赋值前判断年龄是否合法
if (age>130 || age<0){
System.out.println("请输入正确的年龄");
}else{
this.age = age;
}
}
public boolean isFlag() { //注意:boolean类型的属性get方法是is开头的
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
//重写Object类中的toString方法
@Override
public String toString() {
return "Person{" +"name=" + name +", age=" + age+ '}';
}
}
多态
多态的概念
-
**概念:**同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。简单的说:就是用父类的引用指向子类的对象。
-
我们经常说“哺乳动物有很多种叫声”,如”汪汪”,”喵喵”,”嗷呜”等,这就是哺乳动物叫声的多态,多种形态。
-
当一个类有很多子类时,并且这些子类都重写了父类中的某个方法。那么当我们把子类创建的对象的引用放到一个父类的对象中时(即上转型对象),那么这个上转型对象调用这个方法时就具有多种形态,因为子类在重写父类方法时可能生产不同行为。
-
多态就是指父类的某个方法被子类重写时,可以各自产生自己的功能行为。
-
Java绑定原理
- Java中有两种绑定机制:静态绑定和动态绑定
- 编译期间进行的绑定(静态绑定或者前期绑定):对象的属性以及对象的类方法与变量的声明类型进行绑定叫做静态绑定或者前期绑定。
- Java在运行时动态决定实例方法的调用叫做后期绑定(late binding):根据具体引用的实体来决定实例方法的调用叫运行期绑定,正是因为有了动态绑定机制才能实现Java中的多态行为。
多态的使用要点
- 多态是方法的多态,不是属性的多态(多态与属性无关)。
- **多态的存在要有3个必要条件:**继承,方法重写,父类引用指向子类对象。
- 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
对象的转型
- 向上转型的特点
- 父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。
- 不能操作子类新增的成员变量和方法;
- 可以操作子类继承的变量,也可操作子类继承或重写的实例方法;
- 如果子类重写了父类的某个方法后,当对象的上转型对象调用这个方法时一定是调用了这个重写的方法。
- 上转型对象向下转型
-
不要将父类创建的对象和子类对象上转型对象混淆;
-
对象的向下转型:可以将对象的上转型对象再强制转换到一个子类对象,这时该子类对象又具备子类所有属性和功能;(即下溯)
Animal animal = new Tiger();//上转型对象
Tiger tiger = (Tiger)animal;//下溯写法
- **注意:**不可以将父类创建的对象的引用赋值给子类声明的对象(不能说”哺乳动物是猫”);
instanceof运算符
instanceof是二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false。使用instanceof关键字用来判断当前对象是否是类的一一个实例。
可以以一下动物的例子理解多态以及对象的转型,例如:
Animal animal = tiger;
if (animal instanceof Cat){ //animal instanceof Cat判断animal是否是Cat的实例
Cat cat = (Cat) animal;
} else if (animal instanceof Tiger) {
Tiger tiger = (Tiger) animal;
tiger.shout();
} else {
System.out.println("animal不是Tiger的上转型对象");
}
类型自动提升及强制类型转换条件
- 创建对象的类之间存在继承关系。
- 向下转型应该加一个条件:必须是该子类的上转型对象才能被成功下溯成子类对象
多态的例子
public class PolymorphismTest {
//有了多态,只需要让增加的这个类继承Animal类就可以,这样就提高了代码的复用性了。
//如果没有多态,每增加一种动物,就需要重载一种动物的喊叫方法,非常麻烦。
public static void animalShout(Animal a) {
a.shout();
}
public static void main(String[] args) {
Animal a1 = new Tiger();
//传的具体是哪一个类就调用哪一个类的方法,大大提高了程序的可扩展性。
animalShout(a1);
Animal a2 = new Cat();
animalShout(a2); //a2为编译类型,Dog对象才是运行时类型。
//a1.Hunting(); 不能操作子类新增的成员变量和方法,需要先将对象向下转型
//编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换,否则通不过编译器的检查。
Tiger t=(Tiger)a1; //向下需要强制类型转换
t.Hunting();
System.out.println("--------下部分为对象的转型---------");
Animal animal = new Tiger(); //可以向上自动转型
if (animal instanceof Cat){ //animal instanceof Cat判断animal是否是Cat的实例
Cat cat = (Cat) animal;
} else if (animal instanceof Tiger) { //animal instanceof Tiger判断animal是否是Tiger的实例
Tiger tiger = (Tiger) animal;
tiger.shout();
} else {
System.out.println("animal不是Tiger的上转型对象");
}
}
}
//Animal为父类,Tiger和Cat为Animal的子类
class Animal{
public void shout(){
System.out.println("吼叫");
}
}
class Tiger extends Animal {
@Override
public void shout() {
System.out.println("嗷呜");
}
public void Hunting(){
System.out.println("捕猎");
}
}
class Cat extends Animal{
@Override
public void shout() {
System.out.println("喵喵喵");
}
}
下一篇: Spring官方文档(中文版!!!)