面向对象三大特性:封装、继承、多态。
- 在对象的外部,为对象的属性赋值,可能存在非法数据的录入。
- 就目前的技术而言,并没有办法对属性的赋值加以控制。
概念:尽可能隐藏对象的内部实现细节,控制对象的修改及访问的权限。
访问修饰符:private (可将属性修饰为私有,仅本类可见)
以访问方法的形式,进而完成赋值与取值操作。
问题:依旧没有解决到非法数据录入!
- 提供公共访问方法,以保证数据的正常录入。
- 命名规范:
- 赋值:setXXX() //使用方法参数实现赋值
- 取值:getXXX() //使用方法返回值实现取值
在公共的访问方法内部,添加逻辑判断,进而过滤掉非法数据,以保证数据安全。
// equipId的 setXXX 赋值方法
public void setEquipId(int equipId) {
//对传过来的值 进行 具体业务需求的操作和修改
if(equipId >100)
{
this.equipId = 100;
}
this.equipId = equipId;
}
get/set方法是外界访问对象私有属性的唯一通道,方法内部可对数据进行检测和过滤。
package com.qfedu;
public class Equip {
//装备id
private int equipId;
//装备名
private String equipName;
//别名
private String equipAsname;
//属性id
private int attrId;
//属性名称
private String attrName;
//售价
private int sellingPrice;
//买价
private int buyingPrice;
//详情
private String detail;
//被动效果
private String uniquePassive;
//装备图片
private String imageUrl;
// equipId的 setXXX 赋值方法
public void setEquipId(int equipId) {
this.equipId = equipId;
}
// equipId的 取值方法
public int getEquipId() {
return equipId;
}
// equipName setXXX 赋值方法
public void setEquipName(String equipName) {
this.equipName = equipName;
}
// equipName的 取值方法
public String getEquipName() {
return equipName;
}
// equipAsname setXXX 赋值方法
public void setEquipAsname(String equipAsname) {
this.equipAsname = equipAsname;
}
// equipAsname 取值方法
public String getEquipAsname() {
return equipAsname;
}
public void setAttrId(int attrId) {
this.attrId = attrId;
}
public int getAttrId() {
return attrId;
}
public String getAttrName() {
return attrName;
}
public void setAttrName(String attrName) {
this.attrName = attrName;
}
public int getSellingPrice() {
return sellingPrice;
}
public void setSellingPrice(int sellingPrice) {
this.sellingPrice = sellingPrice;
}
public int getBuyingPrice() {
return buyingPrice;
}
public void setBuyingPrice(int buyingPrice) {
this.buyingPrice = buyingPrice;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public String getUniquePassive() {
return uniquePassive;
}
public void setUniquePassive(String uniquePassive) {
this.uniquePassive = uniquePassive;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}
package com.qfedu;
public class EquipTest {
public static void main(String[] args) {
Equip equip = new Equip();
//setXXX 赋值
equip.setEquipId(1112);
equip.setEquipName("匕首");
//......
System.out.println("equipId="+equip.getEquipId()+"equipName="+equip.getEquipName() +".......");
}
}
- 生活中的“继承”是施方的一种赠与,受方的一种获得。
- 将一方所拥有的东西给予另一方。
- 程序中的继承,是类与类之间特征和行为的一种赠与或获得。
- 两个类之间的继承关系,必须满足“is a”的关系。
实战:可根据程序需要使用到的多个具体类,进行共性抽取,进而定义父类。
在一组相同或类似的类中,抽取出共性的特征和行为,定义在父类中,实现重用。
语法: class 子类 extends 父类{ } //定义子类时,显示继承父类
应用:产生继承关系之后,子类可以使用父类中的属性和方法,也可定义子类独有的属性和方法。
好处:既提高代码的复用性,又提高代码的可扩展性。
package com.qfedu.extendsTest;
public class Animal {
//品种
String breed;
//年龄
int age;
//性别
String sex;
public void eat() {
System.out.println("动物吃的方法。。。。");
}
public void sleep() {
System.out.println("动物在睡大觉....");
}
}
package com.qfedu.extendsTest;
//狗和动物是 is a 的关系
//所以可以存在 继承关系
// 子类 extends 父类
public class Dog extends Animal {
String furColoer;
public void run() {
System.out.println("狗在溜达。。。。。");
}
}
package com.qfedu.extendsTest;
public class Bread extends Animal {
String furColoer;
public void fly() {
System.out.println("自由的飞......");
}
}
package com.qfedu.extendsTest;
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.furColoer = "白白";
dog.breed = "哈士奇";
dog.age =12;
dog.sex="母";
System.out.println(dog.sex);
dog.eat();
dog.sleep();
dog.run();
Bread bread = new Bread();
bread.age=300;
bread.breed="雕";
bread.furColoer="黑不溜秋";
bread.fly();
System.out.println(bread.furColoer);
}
}
Java为单继承,一个类只能有一个直接父类,但可以多级继承,属性和方法逐级叠加。
构造方法: 类中的构造方法,只负责创建本类对象,不可继承。
private修饰的属性和方法:访问修饰符的一种,仅本类可见。(详见下图)
父子类不在同一个package中时,default修饰的属性和方法:访问修饰符的一种,仅同包可见。(详见下图)
思考:子类中是否可以定义和父类相同的方法?
思考:为什么需要在子类中定义和父类相同的方法?
分析:当父类提供的方法无法满足子类需求时,可在子类中定义和父类相同的方法进行重写(Override)。
方法重写原则:
- 方法名称、参数列表、返回值类型必须与父类相同。
- 访问修饰符可与父类相同或是比父类更宽泛。
方法重写的执行:
- 子类重写父类方法后,调用时优先执行子类重写后的方法。
package com.qfedu.extendsTest;
//狗和动物是 is a 的关系
//所以可以存在 继承关系
// 子类 extends 父类
//单继承
public class Dog extends Animal {
String furColoer;
public void run() {
System.out.println("狗在溜达。。。。。");
}
//重写 eat 和 sleep方法
// 1.返回值 方法名(参数列表) 要父类完全相同
// 2. 重写方法修饰符 >= 父类的修饰符 权限
// 3. 调用子类的,--》 如果子类没有,再调用父类 的
@Override // 重写的注解标记
public void eat() {
System.out.println("狗爱吃骨头.......");
}
public void sleep() {
System.out.println("狗在睡大觉。。。。");
}
}
在子类中,可直接访问从父类继承到的属性和方法,但如果父子类的属性或方法存在重名(属性遮蔽、方法重写)时,需要加以区分,才可专项访问。
super关键字可在子类中访问父类的方法。
- 使用”super.”的形式访问父类的方法,进而完成在子类中的复用;
- 再叠加额外的功能代码,组成新的功能。
父子类的同名属性不存在重写关系,两块空间同时存在(子类遮蔽父类属性),需使用不同前缀进行访问。
package com.qfedu.extendsTest;
public class Fish extends Animal{
//年龄
int age = 10;
public void swim() {
System.out.println("小鱼游来游去");
}
public int getAge() {
//1. 要的是本类里的age
//2 用super来获取 父类 属性
return super.age;
}
@Override
public void eat() {
//1.调用原来的方法
super.eat();
//2.增加自己的逻辑
System.out.println("虾米");
}
@Override
public void sleep() {
//1.调用父类 的方法
super.sleep();
//2.增加自己的逻辑
System.out.println("睡7秒");
}
}
//super关键字
//1. super 访问父类 的方法和属性
//2. super访问属性 如果子类与父类 的属性有重复, 用super来表示父类 的属性
System.out.println("--------------------------------------");
Fish fish = new Fish();
fish.eat();
fish.sleep();
System.out.println(fish.getAge());
在具有继承关系的对象创建中,构建子类对象会先构建父类对象。
由父类的共性内容,叠加子类的独有内容,组合成完整的子类对象。
构建子类对象时,先构建父类对象。
运行结果:
A()
B()
C()
super():表示调用父类无参构造方法。如果没有显示书写,隐式存在于子类构造方法的首行。
super():表示调用父类无参构造方法。
super(实参):表示调用父类有参构造方法。
public class TestArgsConstructor {
public static void main(String[] args) {
new Son();
System.out.println("-------------");
Son son = new Son(10);
System.out.println("-------------");
new Son(3.5);
System.out.println(son.field);
}
}
class Father{
int field;//父类提供的属性
public Father() {
System.out.println("Father() - 无参构造被执行");
}
public Father(int a) {
this.field = a;
System.out.println("Father(int a) - 一参构造被执行");
}
}
class Son extends Father{
public Son() {
//super();
System.out.println("Son() - 无参构造被执行");
}
public Son(int b) {
super(b);
System.out.println("Son(int b) - 一参构造被执行");
}
public Son(double c) {
super(1);
System.out.println("Son(double c) - 一参构造被执行");
}
}
运行结果:
A-无参构造
B-无参构造
B-有参构造
this或super使用在构造方法中时,都要求在首行。
当子类构造中使用了this()或this(实参),即不可再同时书写super()或super(实参),会由this()指向z构造方法完成super()调用。
package com.qfedu.extendsTest;
//调用父类的构造方法
//1. 创建子类对象时 默认调用父类 的无参构造器,除非 调用有参的super
public class Pig extends Animal{
String weight;
//无参构造器
public Pig() {
super();//默认有
System.out.println("pig的无参构造器。。。。。");
}
public Pig(int age,String sex,String weight) {
//super(age,sex); //调用 父类 的有参,如果不写。默认是super();
this();
this.weight = weight;
}
public static void main(String[] args) {
//Pig pig = new Pig();
Pig pig1 = new Pig(1,"公","300斤");
}
}
生活中,不同人物角色看待同一个对象的视角不同,关注点也不相同。
生活中的多态是指“客观事物在人脑中的主观反应”。
主观意识上的类别与客观存在的对象具有“is a”关系时,即形成多态。
概念: 父类引用指向子类对象,从而产生多种形态。
二者具有直接或间接的继承关系时,父类引用可指向子类对象,即形成多态。
父类引用仅可调用父类所声明的属性和方法,不可调用子类独有的属性和方法。
思考:如果子类中重写了父类中的方法,以父类类型引用调用此方法时,优先执行父类中的方法还是子类中的方法?
实际运行过程中,依旧遵循重写原则,如果子类重写了父类中的方法,执行子类中重写后的方法,否则执行父类中的方法。
方法重载可以解决接收不同对象参数的问题,但其缺点也比较明显。
- 首先,随着子类的增加,Master类需要继续提供大量的方法重载,多次修改并重新编译源文件。
- 其次,每一个feed方法与某一种具体类型形成了密不可分的关系,耦合太高。
场景一: 使方法参数的类型更为宽泛。
场景二:使用父类作为方法返回值实现多态,使方法可以返回不同子类对象。
package com.qfedu.extendsTest;
import java.io.DataOutput;
import java.io.File;
import java.lang.reflect.AnnotatedArrayType;
//1 写一个方法,传入 子类的对象 输出 各个子类的品种
public class Test1 {
public static void main(String[] args) {
//1 创建一个实例
Test1 test1 = new Test1();
//2. 子类 dog
//Dog dog = new Dog();
//1定义体现: 父类类型 对象名 = new 子类();
Animal animal1 = new Dog();
animal1.breed="狗";
test1.printBreed(animal1);
animal1.eat();
//2. 子类 dog
//Cat cat = new Cat();
Animal animal2 = new Cat();
animal2.breed="猫";
test1.printBreed(animal2);
animal2.eat();
//......
//测试多态做为返回值
Animal animal = test1.getAnimal();
Dog dog = (Dog)animal;
//Cat cat = (Cat)animal; //转换错误 里边放的是Dog
System.out.println(animal.breed);
//
System.out.println((dog instanceof Animal));
System.out.println((animal2 instanceof Animal));
//System.out.println((test1 instanceof Animal));
}
public void printBreed(Animal animal) {
System.out.println(animal.breed);
}
public Animal getAnimal() {
Animal animal = new Dog();
return animal;
}
/* //1 Dog
public void printBreed(Dog dog) {
System.out.println(dog.breed);
}
//2. Cat
public void printBreed(Cat cat) {
System.out.println(cat.breed);
}
//3.Bread
public void printBreed(Bread bread) {
System.out.println(bread.breed);
}
//4.Fish
public void printBreed(Fish fish) {
System.out.println(fish.breed);
}
//5.Pig
public void printBreed(Pig pig) {
System.out.println(pig.breed);
}
*/
}
父类引用中保存真实子类对象,称为向上转型(即多态核心概念)。
注意: 仅可调用Animal中所声明的属性和方法。
将父类引用中的真实子类对象,强转回子类本身类型,称为向下转型。
注意:只有转换回子类真实类型,才可调用子类独有的属性和方法。
向下转型时,如果父类引用中的子类对象类型和目标类型不匹配,则会发生类型转换异常。
向下转型前,应判断引用中的对象真实类型,保证类型转换的正确性。
语法: 父类引用 instance of 类型 //返回boolean类型结果