面向对象的三大特性
面向对象的三大特性,分别为:封装、继承、多态。
一、封装性
1. 封装性概念
简言之就是隐藏该隐藏的,暴露该暴露的。
一般情况下,我们将我们类中的属性设为private,方法设为public。并为private属性提供set和get方法。
事实上,对象的出现就是为了封装数据的,每个对象都封装自己的数据。
2. 修饰符的范围
修饰符的范围可见下表:
3. static修饰符
如果说,封装性是我们每个对象自私、独享的一面,那么static便是对象无私、共享的一面。
static可以修饰类中的属性和方法。被static修饰的属性和方法,是所有该类的对象所共享的。这个共享表现在两个方面:
- 被static修饰的属性的值,在所有变量中共享,也就是说,你改变一个对象中的static属性时,其他的对象中的该属性值也会随着改变。
- 即使在还没有创建该类的对象时,就可以通过类名.属性名,或类名.方法名()的方式访问static修饰类的属性。也正是因为这样,static类型的方法只能访问static类型的属性,因为在对象还未创建时,非static类型的属性还不存在。
正是因为static修饰的方法可以在没有类对象的时候被执行,所以我们的main方法需要被static修饰。
新建一个Person类,添加static类型的属性(并创建其set、get方法)和方法:
package com.daw.test2;
public class Person {
private String id;
private String name;
private int age;
//static修饰变量
static String nation;
//static修饰方法
public static void test() {
System.out.println(nation);
//System.out.println(id); //static的方法只能访问static的对象
}
//方法
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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 String getNation() {
return nation;
}
public void setNation(String nation) {
this.nation = nation;
}
}
编写类Test2来尝试访问static修饰的对象和方法
package com.daw.test2;
public class Test2 {
public static void main(String[] args) {
//在未创建Person类的对象时,即可访问static修饰的属性
Person.nation = "中国";
System.out.println(Person.nation);
Person.test();
System.out.println("******************");
Person p1 = new Person();
Person p2 = new Person();
System.out.println(p1.nation); //nation不再是private类型变量,可以外部直接访问
System.out.println(p2.nation);
System.out.println("******************");
p2.nation = "中华人民共和国";
System.out.println(Person.nation);
System.out.println(p1.nation);
System.out.println(p2.nation);
System.out.println("******************");
}
}
可见,当修改了p2中的nation属性值以后,类和p1的也跟着改变了。
二、继承
1. 什么是继承
比如我们人,是一种动物,具有动物的所有特征和特性;轿车是汽车,大巴也是汽车,它们也都具有汽车的所有特征。在Java中,我们将这种关系称之为继承。
定义
:继承就是子类继承父类的特征(属性)和行为(方法),使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
我们现在只需要知道,
父类
是原有的,子类
是在父类基础上的延伸,至于上一句话什么意思,通过代码来学习。
2. 子类对象继承父类对象的全部
属性和方法。
我们新建一个包,再其中创建一个类Animal,使之具有 属性age 和 方法sleep。
package com.daw.test3;
public class Animal {
int age;
public void sleep() {
System.out.println("休息。。。");
}
}
假如我们需要定义一个类person,它包含Animal所具有的的所有的属性和方法,那么我们就可以让它继承Animal。
事实上,java中使用的是extend,翻译为扩张,比起继承,显然扩张更符合这个功能的原本含义。BUT…
想要让一个类继承另一个类,有两种办法。
1)继承方法一:在创建类时使其继承另一个类
在我们创建类时,有Superclass一项,此项为选择所创建类的父类,我们点击后面的Browse,或者手动写上我们的所要继承的类的 包名.类名。
在弹出的界面输入我们要继承的类名,并选择我们要继承的类(注意后面的包名要对应)。
完成创建以后,我们便成功继承了的Animal类。
2)继承方法二:在创建类以后手动添加extend
由上面最终创建的Person类,很多人会发现,不就是多了一个extends Animal
吗,直接手动写上岂不是更加更方便。其实是可以的。
但是需要注意:
- 父类和子类在同一个包中,可以直接写extends 父类
- 父类和子类不在同一个包中,例如,我希望我的Person类继承我们之前的com.daw.test2中的Car类。在写完extends Car以后,一定要选择正确的包。
选择完成后,会有import 包名.类名;
我们之前在需要从键盘获取数据时,使用过Scanner,当时也会产生一行import java.util.Scanner;
在我们的类中,使用其他包中的类中的属性或方法时,我们就需要用import来告诉我们的程序,这些类、属性、方法是从哪里来的。这也是包存在的意义之一。
当然,我们这里让Person继承Car类只是为了补充一部分包的内容,关于包的内容还有很多,我们慢慢的补充。
我们把Person类的继承关系改回Animal。创建新的类Test1来实验一下。
package com.daw.test3;
public class Test1 {
public static void main(String[] args) {
Animal animal = new Animal();
animal.age = 1;
System.out.println(animal.age);
animal.sleep();
System.out.println("************");
Person person = new Person();
person.age = 1;
System.out.println(person.age);
person.sleep();
}
}
运行结果可见,虽然我们并没有给Person类写内容,但是Person类的对象也拥有Animal类的属性和方法。
3. 子类对象可以通过重写(override,又称覆盖)来自定义父类中的方法。
很多时候,我们在父类中的方法可能并不能满足我们子类的需求,这时候我们就要使用重写。
在Person类中重写sleep方法。
注意:
- 在重写方法时,方法名、返回值和参数列表要相同,如果只是方法名相同,返回值和参数列表不同,则为重载,不能加@override。
- 重写方法时,可以自己敲,也可以利用鼠标右键-> Source -> Override/Implement Methods来生成我们要重写的方法。
package com.daw.test3;
public class Person extends Animal {
//重载:不能加@Override
public void sleep(String name){
System.out.println(name + "睡觉。。。");
}
//重写:要加@Override
@Override
public void sleep() {
System.out.println("上床睡觉。。。");
}
}
重写运行Test1。
4. 子类中可以定义新的属性和方法
在子类中,我们不止可以重写父类中的方法,我们还可以添加新的属性和方法。比如人比起动物,人有名字,可以工作。我们因此给person添加新的属性和方法。
String name;
public void work(){
System.out.println("工作。。。");
}
我们在Test1中尝试调用person类新加入的属性和方法
person.name = "五代";
System.out.println(person.name);
person.work();
运行后可以成功输出我们的属性,也可以成功运行我们的方法。
5. 多级继承
我们继承了其他类的类,也可以被继承。比如我们定义一个Student类,使其继承Person类。方法与之前完全相同。此时Animal类,即为Student类的间接父类。
6. Object类和java.lang包
在我们创建类时,发现父类,默认为java.lang.Object。
Object类是java.lang包中的类,是所有类的父类。
注意:
- 如果一个类没有指定父类,那么这个类的父类的Object类;
- 如果一个类指定了父类,那么这个类的间接父类的Object类;
java.lang包是Java提供给我们的工具包,其中包含了我们java代码中最最基础的一些操作。也正因如此,java.lang包也是唯一不需要导入(import)即可以使用的包。
除了lang包外,java还为我们提供了现成的工具类,比如Scanner所在的java.util等等。我们不需要所有的都掌握,当我们需要某种功能时,我们可以去搜索,一般即可找到对应的类和其使用方法。在需求中学习即可。
大家可以通过搜索器搜索API文档(API 为 应用程序编程接口,指一些预先定义好的类),来查看对应的JDK版本中Java为我们提供的方法及其使用方法。其中在 在线API文档 网站中:
1)左上角---包
2)左下角---包中的类和接口
3)右侧-----类的详细信息
|-----构造方法 Constructor
|-----属性 Field
|-----方法 Method
|---返回值类型
|---方法名
|---参数列表
|---概要信息
三、多态
1. 什么是多态
学生是人,老师也是人。因此,当我们有一个“人”的对象时,它可以为“老师”,也可以为“学生”,可以表现为多种形态。这就是多态性。
在Java中,我们将子类对象
赋值给父类引用
(父类引用
指向子类对象
),来实现多态。
列如:
Person p = new Student();
2. 多态性的特点
- 编译时,只能访问左边类(父类)具有的属性和方法
- 运行时,方法访问右边类(子类)具有的属性和方法
为了表现出我们的 student类 和 Person类 的不同,在student中添加属性stuId表示学号、添加learn方法,并重写work、sleep方法。
package com.daw.test3;
public class Student extends Person {
String stuId;
public void learn(){
System.out.println("上课。。。");
}
@Override
public void work(){
System.out.println("写作业。。。");
}
@Override
public void sleep(){
System.out.println("学生睡觉。。。");
}
}
我们新建一个test2来测试一下。首先我们尝试访问student中的属性,发现报错。表明特点一符合,无法使用子类中的属性和方法(方法自习测试)。
我们再实验特点二,访问其work方法。
package com.daw.test3;
public class Test2 {
public static void main(String[] args) {
Person person = new Student();
// person.stuId; //报错,仅能使用指针所属的类中的属性和方法。
person.work();
}
}
输出如下。表明,在运行过程中,访问的是对象所属类中的属性和方法。
3. instanceof运算符
instanceof用于判断一个类是否属于某种类,返回 boolean类型。
用法:对象 instanceof 类名
if (person instanceof Student) {
System.out.println(1);
}
if (person instanceof Person) {
System.out.println(2);
}
判断结果表明,person既属于student,又属于person(子类对象既属于子类,又属于父类;而父类对象只属于父类。),这也表明了,在运行时,是以实际对象所属的类进行操作的。
4. 多态的转型
- 向上转型 -> 通过
子类对象
实例化父类对象
,这种属于自动转换 - 向下转型 -> 通过
父类对象
实例化子类对象
,这种属于强制转换
多态就属于向上转型。这种转型是自然的,无条件的。所有的子类对象,都可以被赋值给其父类指针。
而在有些时候,我们需要使用一些子类中特有的属性和方法时,我们就不得不再将这一过程逆回去,也就是向下转型。
注:向下转型的要求为,父类对象实际所属的类,应当为
所要转型类的类
或其子类
。
创建一个类Test3。
package com.daw.test3;
public class Test3 {
public static void main(String[] args) {
//向上转型
Animal a = new Student();
//向下转型1 --- 实际对象所属的类为转型类的子类
Person p = (Person) a;
p.age = 5;
System.out.println(a.age);
System.out.println(p.age);
p.sleep();
向下转型2 --- 实际对象所属的类为转型类
Student s = (Student) a;
s.age = 10;
System.out.println(p.age);
System.out.println(s.age);
}
}
由结果可知,a、p和s的age是同一个,因此,a、p和s是指向同一区域的引用,而非复制出一个新的对象。
5. 子类对象的多态性的应用
继承于同一父类的对象,可以依靠引用父类,将相似的方法写成一个。而不必以每种类型进行方法的重载。
例如,我们创建一个Teacher类,也继承于Person类,为其添加一个Teach方法。
package com.daw.test3;
public class Teacher extends Person {
public void teach(){
System.out.println("教课。。。");
}
}
此时,我们希望在test4完成开始上课这一动作,如果是老师,则执行其teach方法,如果是学生,则执行其learn方法。
如果我们不使用多态,则需要写一个参数为Teacher的方法,再重载一个参数为Studen类型的方法。而使用多态则只需要一个程序即可。
package com.daw.test3;
public class Test4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s = new Student();
test(s);
}
public static void test(Person person) {
if (person instanceof Teacher) {
Teacher teacher = (Teacher) person;
teacher.teach();
}else if (person instanceof Student) {
Student student = (Student) person;
student.learn();
}
}
}