Java面向对象编程
1. 封装----------------------------------------------------->
2. 继承----------------------------------------------------->
3. 多态----------------------------------------------------->
4. 接口----------------------------------------------------->
>>>封装
·封装是什么?
封装是类的三大特性之一。顾名思义,封装表示将东西打包,使其 以新的完整的形式出现。
在面向对象编程中,封装可以理解为,将数据和操作方法保存在一起的技术,或是有选择地隐藏或公开类中的属性和方法的过程。封装的具体实现是将成员变量、方法和属性以类的形式进行封装,在类或对象之间通过消息传递进行相互交流,而传递消息是通过调用类公开的方法完成的。
总的来说,就是将类的状态信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。
·为什么用封装?
我们通过如下例子来解释:
Class Student{
public static void main(String[] args){
private String name;//姓名
public int age=19;//年龄
public String cardId=”440923199110094375”;//身份证号
public void setName(String name){//初始化学员的姓名
this.name=name;
}
public void print(){//打印学员信息
System.out.println(“大家好,我叫”+this.name+”,今 年”+this.age+”岁,身份证号是:”+this.cardId+””);
}
}
}
如果在上面的例子中Student类定义的基础上,编写创建一个Student类的实例stu,并重新对该对象的年龄age和身份证号cardId进行赋值。
Student stu = new Student();
stu.setName(“依依”);
stu.age= -10;
stu.cardId=”123456789”;
stu.print();
程序运行之后学员对象stu的年龄变成了“-10”,身份证号也被随意改为“123456789”。这种随意修改对象公有变量的赋值是完全不符合逻辑的,那么,如何才能保证程序数据的安全性能?有没有办法限制敏感属性的数据更新操作呢?
年龄age是否能限制属性值范围为1~100?
身份证号cardId是否能限制属性值为18位的有一定规定格式的?
你可能说把属性age设置成私有的就可以解决这个问题。但是,请注意在上面问题中给出的代码对属性age的赋值是在Student类外实现的,因此这个方案不可行。我们知道,类成员包括变量和方法。那么,让我们通过类方法来解决这个问题。我们可以定义一个带参数的方法来实现。实现思路是:把输入的年龄作为这个方法的参数传递到方法中,在方法中使用if结构对年龄的取值进行判断。实现的代码如下:
Class Student{
private String name;//姓名
private int age=19;//年龄
private String cardId=”440923199110094375”;//身份证号
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>0&&age<=100){
this.age=age;
}else{
this.age=23;
}
}
public String getCardId(){
return cardId;
}
public void setCardId(String cardId){
//省略一些实现代码
this.cardId=cardId;
}
}
Class Test{
public static void main(String[] args){
Student stu=new Student();
stu.setName(“依依”);
stu.setAge(-10);
stu.setCardId(“123456789”);
System.out.println(“大家好,我叫”+getName()+”,今 年”+getAge()+”岁,身份证号是:”+getCardId()+””);
}
}
·封装的好处是什么?
封装的最大好处就是安全性。
封装的好处主要有:
·隐藏类的实现细节
·让使用者只能通过程序员规定的方法来访问数据
(避免使用非法数据赋值)
·可以方便地加入存取控制语句,限制不合理操作
(保证数据的完整性)
·避免类内部发生修改时,导致整个程序的修改
·封装怎么用?
在Java中已经考虑到上面例子的这种情况,解决途径就是对类进行封装,通过private、protecl、public和默认权限控制符来实现权限控制。在此例中,我们将属性均设为private权限,将只在类内可见。然后再提供public权限的setter方法和getter方法实现对属性的存取,在serter方法中对输入的属性值的范围进行判断。
封装的具体步骤:
1、修改属性的可见性来限制对属性的访问;
2、为每个属性创建一对赋值(setter)方法和取值(getter)方法,用于对这些属性的存取;
3、在赋值方法中,加入对属性的存取控制语句。
封装时会用到多个权限控制符来修饰成员变量和方法,区别如下:
·private:成员变量和方法只能在类内被访问,具有类内可见性。
·默认:成员变量和方法只能被同一个包里的类访问,具有包可见性。
·protected:可以被同一个包中的类访问,被同一个项目不同包中的子类访问。
·public:可以被同一个项目中所有类访问,具有项目可见性,这是最大的访问权限。
·封装的总结:
封装就是将类的成员属性声明为私有的,同时提供公有的方法实现对该成员属性的存取操作。
封装的好处主要有:隐藏类的实现细节;让使用者只能通过程序员规定的方法来访问数据(避免使用非法数据赋值);可以方便地加入存取控制语句,限制不合理操作(保证数据的完整性);避免类内部发生修改时,导致整个程序的修改。
封装是面向对象的重要内容,也是面向对象的三大特性之一。它用于隐藏内部实现,对外只暴露对类的基本操作,而不会让其他对象影响类的内部实现。
>>>继承
·继承是什么?
继承是类的三大特性之一,是Java中实现代码重用的重要手段之一。Java中只支持单继承,即每个类只能有一个直接父类。继承表达的是is a的关系,或者说是一种特殊和一般的关系,例如Dog is Pet。同样我们可以让学生继承人,让苹果继承水果,让三角形继承几何图形。
·为什么用继承?
我们打个例子,假设我们的项目中有两个类,一个Dog类和一个Penguin类,在这两个类中有许多相同的属性和方法。例如name、health和love属性以及相应的getter方法和setter方法,还有打印信息的print()方法。这样设计的不足之处主要表现在两方面:一是代码重复,二是如果要修改的话,两个类都要修改,如果涉及的类较多,那修改量就更大了。如何有效地解决这个问题呢?
用继承就可以很好地解决这个问题。
我们可以将Dog类和Penguin类中相同的属性和方法提取出来放到一个单独的Pet类中,然后让Dog类和Penguin类继承Pet类,同时保留自己特有的属性和方法,这样就让我们的程序员减少了不少重复写代码而带来的疲劳,同时使代码也更加简洁、美观。
·继承的好处是什么?
·减少代码量,使代码更加简洁
·实现代码重用
·提高代码的可维护性
·继承怎么用?
在Java中,继承(Inheritance)通过extends关键字来实现,其中Dog和Penguin称为子类,Pet称为父类、基类或超类。修饰符如果是public,该类在整个项目中可见,不写public修饰符则该类只在当前包可见;不可以使用private和protected修饰类。
在Java中,所有的Java类都直接或间接地继承了java.lang.Object类。Object类是所有Java类的祖先。在定义一个类时,没有使用extends关键字,那么这个类直接继承Object类。例如:public class MyObject{}这段代码表明:MyObject类的直接父类为Object类。
在Java中,子类可以从父类中继承到哪些“财产”呢?
·继承public和protected修饰的属性和方法,不管子类和父类是否在同一个包里。
·继承默认权限修饰符修饰的属性和方法,但子类和父类必须在同一个包里。
·无法继承private修饰的属性和方法。
·无法继承父类的构造方法。
重写和继承关系中的构造方法:
1、子类重写父类:
由于从父类继承的方法不能满足子类的需求,从而引入了继承关系中的“重写”,在子类中可以对父类的同名方法进行重写(覆盖),以符合需求。
在Java中,是这样定义“重写”的:在子类中可以根据需求对从父类继承的方法进行重新编写,称为方法的重写或方法的覆盖(overriding)。
方法重写必须满足如下要求:
·重写方法和被重写方法必须具有相同的方法名。
·重写方法和被重写方法必须具有相同的参数列表。
·重写方法的返回值类型必须和被重写方法的返回值类型相同或者是其子类。
·重写方法的不能缩小被重写方法的访问权限。
如果在子类中想调用父类的被重写的方法,可以在子类方法中通过“super.方法名”来实现。
super 代表对当前对象的直接父类对象的默认引用。
在子类中可以通过super关键字来访问父类的成员。
·super必须是出现在子类中(子类的方法和构造方法中),而不是其他位置
·可以访问父类的成员,例如父类的属性、方法、构造方法。
·注意访问权限的设置,例如无法通过super访问private成员。
例如,在Dog类中可以通过如下语句来访问父类成员。
·super.name;//访问直接父类的name属性(如果name是private权限,则无法访问)。
·super.print();//访问直接父类的print()方法。
·super(name);//访问直接父类的对应构造方法,只能出现在构造方法中。
2、继承关系中的构造方法:
继承条件下构造方法的调用:
(1)、继承条件下构造方法的调用规则如下:
·如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下,写不写“super();”语句,效果都是一样的。
·如果子类的构造方法中通过super显式调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法。
·如果子类的构造方法中通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则。
·特别注意的是,如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。
(2)、扩展:
·在构造方法中如果有this语句或super语句出现,只能是第一条语句。
·在一个构造方法中不允许同时出现this和super语句(否则就有两条第一条语句)。
·在类方法中不允许出现this和super关键字。
·在实例方法中this和super语句不要求是第一条语句,可以共存。
下面我们来写一个例子,深入理解继承的使用及原理:
/*
*宠物类,是狗狗Dog和企鹅Penguin的父类
*/
public class Pet{
private String name=”无名”;//昵称
private int health=100;//健康值
private int love=0;//亲密度
public Pet(){
this.health=95;
System.out.println(“执行宠物的无参构造方法。”);
}
public Pet(String name){
this.name=name;
}
public String getName(){
return name;
}
public int getHealth(){
return health;
}
public int getLove(){
return love;
}
public void print(){
System.out.println(“宠物的自白:\n我的名字叫”+this.name+”,我的健康值是”+this.health+”,我和主人的亲密度是”+this.love+””);
}
}
public class Dag extends Pet{
private String strain;
public Dog(String name,String strain){
super(name);
this.strain=strain;
}
public String getStrain(){
return strain;
}
public void print(){
super.print();
System.out.println(“我是一只:”+this.strain+””);
}
}
public class Penguin extends Pet{
private String sex;
public Penguin (String name,String sex){
super(name);
this.sex=sex;
}
public String getSex(){
return sex;
}
public void print(){
super.print();
System.out.println(“性别是:”+this.sex+””);
}
}
Class Test{
public static void main(String[] args){
Pet pet=new Pet();
pet.print();
Pet pet2=new Pet(“贝贝”);
pet2.print();
Dog dog=new Dog(“偶偶”,’雪瑞那”);
dog.print();
Penguin pgn=new Penguin(“楠楠”,”Q妹”);
pgn.print();
}
}
抽象类和final
抽象类和抽象方法都通过abstract关键字来修饰。
抽象类不能实例化。
抽象类中可以没有、可以有一个或多个抽象方法,甚至可以全部方法都是抽象方法。
抽象方法只有方法声明,没有方法实现。
有抽象方法的类必须声明为抽象类。
子类必须重写所有的抽象方法才能实例化,否则子类还是一个抽象类。
·“public void print(){}”不是抽象方法,而是有实现但实现为空的普通方法
·“public void print();”是抽象方法,没有大括号,但有分号“;”
·abstract可以用来修饰类和方法,但不能用来修饰属性和构造方法。
final和abstract是功能相反的两个关键字
final可以用来修饰类、方法、和属性,不能修饰构造方法。
用final修饰的类,不能再被继承。
用final修饰的方法,不能被子类重写。
用final修饰的变量将变成常量,只能赋值一次。
·继承的总结:
继承是Java中实现代码重用的重要手段之一。Java中只支持单继承,即一个类只能有一个直接父类。java.lang.Object类是所有Java类的祖先。
在子类中可以根据实际需求对从父类继承的方法进行重新编写,称为方法的重写或覆盖。
子类中重写的方法和父类中被重写方法必须具有相同的方法名、参数列表,返回值类型必须和被重写方法的返回值类型相同或者是其子类。
如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没用通过this显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。
抽象类不能实例化。抽象类中可以没有、可以有一个或多个抽象方法。子类必须重写所有的抽象方法才能实例化,否则子类还是一个抽象类。
用final修饰的类,不能再被继承。用final修饰的方法,不能被子类重写。用final修饰的变量将变成常量,只能赋值一次。
>>>多态
·多态是什么?
简单来说,多态(Polymorphism)是具有表现多种形态的能力的特征。更专业化的说法是:同一个实现接口,使用不同的实例而执行不同的操作。
·为什么用多态?
为了节省代码量。
打个例子:
如果我们领养了一只狗狗,我们就得写个为狗狗写一个喂养狗狗的方法,如果过段时间,我们又领养一只企鹅,我们又得为企鹅写一个喂养企鹅的方法,如果过段时间我们有领养一只兔子呢?我们又照旧写一个吗?你会!我们不会!我们为什么不写一个通用的方法,无论喂养什么宠物都可以,即使再领养多只宠物,也不用那么费力的去重复写喂养的方法了。这样代码不是更加简洁吗?所以,我们应用了多态。无论什么情况,都可以执行喂养这个方法。
·多态的好处是什么?
最大的好处就是节省代码量,提高代码的可扩展性和可维护性。
(1)· 可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆 Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
(2)· 可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存 在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态 功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
(3)· 接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同 接口,由子类来完善或者覆盖它而实现的。例如,假设超类Shape规定了 两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如 Circle和Sphere为了实现多态,可以完善或者覆盖这两个接口方法。
(4)· 灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
(5)· 简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
·多态怎么用?
1、使用父类作为方法形参实现多态
在Java中,使用父类作为方法的形参,是实现和使用多态的主要方式。
打个例子:
class person {//Person类
String name;//姓名
int age;//年龄
public void eat(){//吃饭
System.out.println(“person eating with mouth”);
}
public void sleep(){//睡觉
System.out.println(“sleeping in night”);
}
}
class Chinese extends Person{//中国人类
public void eat(){//覆盖父类的eat()方法
System.out.println(“Chinese eating rice with mouth by chopsticks”);
}
public void shadowBoxing(){//练习太极拳,子类特有的方法
System.out.println(“practice dashadowBoxing every morning”);
}
}
class English extends Person {//英国人类
public void eat(){//覆盖父类的eat()方法
System.out.println(“English eating meat with mouth by knife”);
}
}
class TestEat {//测试类
public static void main(String[] args){//测试不同国家人吃饭
showEat(new Person);
showEat(new Chinese );
showEat(new English );
}
public static void showEat(Person p){//显示不同国家人吃饭
p.eat();
}
}
上面showEat(new Person);相当于Person p=new Person;//子类到父类的转换(向上转型)
showEat(new Chinese );相当于Person p=new Chinese;//子类到父类的转换(向上转型)
showEat(new English );相当于Person p=new English;//子类到父类的转换(向上转型)
小结:(实现多态的三个条件)
(1)、继承的存在(继承是多态的基础,没有基础就没有多态)
(2)、子类重写父类的方法(多态下调用子类重写后的方法)
(3)、父类引用变量指向子类对象(子类到父类的类型转换)
2、子类到父类的转换(向上转型)
如:Person p=new Chinese;、Person p=new English;
向上转型后通过父类引用变量调用的方法是子类覆盖或继承父类的方法,通过父类引用变量无法调用子类特有的方法。
3、父类到子类的转换(向下转型)
上面提到,当向上转型发生后,将无法调用子类特有的方法。但是如果需要调用子类特有的方法是,可以通过把父类再转换为子类来实现。
父类到子类的转换,需要使用instanceof运算符来判断对象的类型。
将一个指向子类对象的父类引用赋给一个子类的引用,称为向下转型,此时必须进行强制类型转换。
instanceof运算符==》
向下转型时,为什么要用instanceof运算符呢?
如果没有转换为真实子类类型,就会出现类型转换异常。
语法: 对象 instanceof 类或接口
该运算符用来判断一个对象是否属于一个类或者实现了一个接口,结果为true或false。在强制类型转换之前通过instanceof运算符检查对象的真实类型,然后再进行相应的强制类型转换,这样就可以避免类型转换异常,从而提高代码健壮性。
·多态的总结:
通过多态可以减少类中代码量,可以提高代码的可扩展性和可维护性。继承是多态的基础,没有继承就没有多态。
把子类转换为父类,称为向上转型,自动进行类型转换。把父类转换为子类,称为向下转型,必须进行强制类型转换。
向上转型后通过父类引用变量调用的方法是子类覆盖或继承父类的方法,通过父类引用变量无法调用子类特有的方法。
向下转型后可以访问子类特有的方法。必须转换为父类指向的真实子类类型,否则将出现类型转换异常ClassCastException。
instanceof运算符用来判断一个对象是否属于一个类或者实现了一个接口。
instanceof运算符通常和强制类型转换结合使用,首先通过instanceof进行类型判断,然后进行相应的强制类型转换。
使用父类作为方法形参是使用多态的常用方式。
>>>接口(未完,下次补充)
·接口是什么?
接口表示一种约定:
接口表示一种能力:
·为什么用接口?
·接口的好处是什么?
·接口怎么用?
·接口的总结: