目录
面向对象三大特性: 封装,继承,多态
一、封装性
描述: 封装之前学习过方法,方法就是一种封装,在面向对象的封装性中,本质上也是讲方法的封装
封装性:
在面向对象中,不要直接调属性,而是通过set/get方法进行封装好处:
结构更清晰,复用性更强,安全性更强
封装步骤:
1.编写set/get方法,注意规范写法,set用于赋值,get用于取值
- 按照项目中完整实体封装的写法,有多少属性,就有多少set/get
- set方法规范:方法名为set+属性名首字母大写,参数名和属性名一致
- get方法规范:方法名为get+属性首字母大写,返回属性值
2.属性私有化
//面向对象案例:张勇在吃饭
//分析:类-Student; 对象-张勇 属性-姓名,年龄 方法-吃
//问题:年龄为负数--- 会出现,程序没问题,数据不合理的情况
class Student{
private String name;
private int age; //属性私有化,外界不能调用,只能在当前类中使用
public Student() {}
public Student(String name,int age) {
this.name = name;
this.setAge(age); //this调方法
}
public void eat() { //功能性的封装
System.out.println(age+"岁的"+name+"正在吃饭~~");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) { //数据(属性)的封装
//注意:此处为了说明安全性,才加的判断;后续没有特殊说明,则直接赋值即可
if(age<=0) { //如果参数有问题,则属性为默认18岁
this.age = 18;
}else { //如果大于0,则直接赋值
this.age = age;
}
}
public int getAge() {
return age;
}
}
public class Test1 {
public static void main(String[] args) {
Student zy = new Student();
zy.setName("张勇");
zy.setAge(-20); //赋值
//zy.age = -1; //从源头上规避直接调属性
System.out.println(zy.getAge()); //取值
zy.eat();
//2.编写带参构造
Student ly = new Student("李勇",-30);
ly.eat();
}
}
二、继承性
理论描述
-
生活中的继承: 子承父业,儿子继承了父亲的资源
-
程序中的继承:子类继承父类的属性和方法
如何设定继承关系?什么类是子类?什么类是父类?
两个类满足“is a”的关系,就有继承关系。
子类"is a" 父类,例如:猴子是动物,猴子是子类 动物是父类
找父类—父类有很多,怎么找直接父类
例如: 狗是动物,也是生物,也是物质
重合点(特征和行为接近)越多,越接近直接父类
重合点越少,越接近Object类(后面会分析-老祖宗类)
例如:狗-姓名,年龄,毛色; 吃,摇尾巴,睡; 动物也类似;所以,动物类是狗类的直接父类
根据继承特点:
只需将子类共性的部分抽取到父类即可;子类直接继承
继承作用:
减少代码量,提高复用性;提高扩展性
继承案例
//面向对象案例出发:狗会跑,鸟会飞,鱼会游;同时它们都能吃能睡;属性都有姓名,年龄;狗和鸟有毛色
//注意:此处侧重点在于继承,暂时不去封装(后面写项目再封装)
//问题:冗余代码特别多;类与类之间完全独立
//找关系:狗是动物,鸟是动物,鱼是动物,都有同一个父类
class Animal{
String name;
int age;
public void eat() {
System.out.println(name+"正在吃...");
}
public void sleep() {
System.out.println(name+"正在睡...");
}
}
class Dog extends Animal{ //Dog继承Animal
String color;
public void run() {
System.out.println(name+"正在跑");
}
}
class Bird extends Animal{ //Bird继承Animal
String color;
public void fly() {
System.out.println(name+"正在飞");
}
}
class Fish extends Animal{ //Fish继承Animal
public void swim() {
System.out.println(name+"正在游");
}
}
public class Test1 {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "旺财"; //调父类的属性
dog.color = "黄色"; //调自身独有属性
dog.run(); //调自身独有方法
dog.sleep(); //调父类方法
System.out.println("--------------");
Bird bird = new Bird();
bird.name = "八哥";
bird.fly();
bird.eat();
System.out.println("--------------");
Fish fish = new Fish();
fish.name = "小金";
fish.swim();
fish.sleep();
}
}
继承特点
在继承中,没有多继承,但有多级继承
多继承:
有多个父类;多级继承:
有父类,父类又有父类…
//案例:爷爷有1000万;爸爸有一辆跑车;儿子有一辆玩具车
class GrandFather{
String name;
public void haveMoney() {
System.out.println(name+"拥有1000万");
}
}
class Father extends GrandFather{ //多级继承
public void haveCar() {
System.out.println(name+"有一辆跑车");
}
}
class Son extends Father/*,GrandFather*/{//没有多继承
public void haveToy() {
System.out.println(name+"有一辆玩具车");
}
}
public class Test2 {
public static void main(String[] args) {
Father father = new Father();
father.name = "大三"; //调爷爷类的属性
father.haveMoney(); //调爷爷类的方法
father.haveCar(); //调自身的方法
//father.haveToy(); //不能调子类的方法
System.out.println("===============");
Son son = new Son();
son.name = "小三";
son.haveMoney(); //调爷爷类的方法
son.haveCar(); //调父类的方法
son.haveToy(); //调自身的方法
}
}
不可继承性(扩)
- 私有的成员
(private)
不能被继承 构造方法
没有继承性- 不同包的
default
权限,没有继承性
构造方法测试
//测试案例:构造方法没有继承性---测带参构造
class A{
public A() {
System.out.println("父类A的无参构造");
}
public A(int a) {
System.out.println("父类A的带参构造");
}
}
class B extends A{
}
public class Test1 {
public static void main(String[] args) {
//new B(6); //构造方法没有继承性,不能调用父类A的带参构造
}
}
权限测试
访问修饰符:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xoXnsDQO-1690793217796)(D:\笔记\千峰(1).assets\image-20230727144656658.png)]
测试:
//----------com.qf.d_quanxian包中的两个类----------
public class Animal { //实体类
private String name; //私有权限
int age; //default权限
protected String sex; //保护权限
public String love; //公开权限
public void eat() {
System.out.println(name); //私有权限,在本类中可以使用
System.out.println(age); //默认权限,在本类中可以使用
System.out.println(sex); //保护权限,在本类中可以使用
System.out.println(love); //公开权限,在本类中可以使用
}
}
public class Test1 { //测试类
public static void main(String[] args) {
Animal animal = new Animal();
//System.out.println(animal.name); //私有权限,在同包不同类中不能使用
System.out.println(animal.age); //默认权限,在同包不同类中可以使用
System.out.println(animal.sex); //保护权限,在同包不同类中可以使用
System.out.println(animal.love); //公开权限,在同包不同类中可以使用
}
}
//----------com.qf.e_quanxian2包中的两个类----------
public class Dog extends Animal {
public void show() {
//System.out.println(name); //私有权限,在不同包子类中不能使用
//System.out.println(age); //default权限,在不同包子类中不能使用
System.out.println(sex); //保护权限,在不同包子类中可以使用
System.out.println(love); //保护权限,在不同包子类中可以使用
}
}
public class Test1 {
public static void main(String[] args) {
Animal animal = new Animal();
//System.out.println(animal.name); //私有权限,在不同包无继承关系中不能使用
//System.out.println(animal.age); //默认权限,在不同包无继承关系中不能使用
//System.out.println(animal.sex); //保护权限,在不同包无继承关系中不能使用
System.out.println(animal.love); //公开权限,在不同包无继承关系中可以使用
}
}
三、多态
一、多态
多态前提:
必须要有继承或接口
程序中的多态:将子类对象当成父类看待,这就是多态;换句话说,父类引用指向子类对象,即形成多态
父类 自定义对象名 = new 子类();
例如:Animal an = new Dog(); //多态核心
多态应用:父类引用指向子类对象,即可调用父类的方法,不能调用子类独有的方法
应用案例
class Animal{
public void eat() {
System.out.println("动物正在吃...");
}
}
class Dog extends Animal{
public void bark() {
System.out.println("旺财正在汪汪汪...");
}
}
public class Test1 {
public static void main(String[] args) {
Animal animal = new Dog(); //多态核心
animal.eat(); //父类引用可调用父类方法
//当成父类看待,则调不了子类方法
//animal.bark(); //父类引用不能调子类独有方法
}
}
二、多态中的重写
- 父类引用指向子类对象,优先调子类重写方法
- 子类有重写方法,优先调子类方法
直接引用多态
直接引用多态的方式调用重写方法
//案例:将猫当成宠物看待,可调用宠物的吃的方法;如果猫有不同吃的行为,则优先调用
class Pet{
public void eat() {
System.out.println("宠物正在吃东西...");
}
}
class Cat extends Pet{
@Override
public void eat() {
System.out.println("猫正在吃...");
}
}
public class Test2 {
public static void main(String[] args) {
Pet pet = new Cat(); //父类引用指向子类对象,优先调子类重写方法
pet.eat(); //子类有重写方法,优先调子类方法
}
}
传参多态
多态的好处:使程序的扩展性,可维护性更强
//案例:主人喂养宠物,猫,狗都是宠物,都有各自吃的行为
//分析:类-宠物类(父),猫类(子),狗类(子),主人类(第三方类); 方法-吃,喂养
//问题:两个宠物,就需要写两个喂养的方法;后面扩展更多宠物,则继续增加喂养方法
//面向对象设计原则:ocp原则-开闭原则
//o-open:对外部扩展的类持开放状态-例如可扩展很多宠物类(扩展性)
//c-close:对内部修改的代码持关闭状态-例如-主人喂养行为不要增加(维护性,灵活性)
class Pet{ //宠物类
public void eat() { //吃
System.out.println("宠物正在吃...");
}
}
class Dog extends Pet{
@Override
public void eat() { //重写吃
System.out.println("旺财正在吃...");
}
}
class Cat extends Pet{
@Override
public void eat() { //重写吃
System.out.println("加菲猫正在吃...");
}
}
class Master{ //主人类
//--------重载方法--------
/*
public void feed(Dog dog) {
dog.eat(); //狗在吃
}
public void feed(Cat cat) {
cat.eat(); //猫在吃
}*/
//传参实现的多态
public void feed(Pet pet) {//Pet pet = new Dog(); 多态
pet.eat(); //谁传给我,就调谁的 (不要看谁调的,要看谁传的)
}
}
public class Test1 {
public static void main(String[] args) {
Master master = new Master();
master.feed(new Dog()); //1.主人喂养狗
master.feed(new Cat()); //2.主人喂养猫
}
}
返回值多态
返回值多态: 调用方法返回子类对象,由父类引用接收;构成返回值多态
//案例:主人购买宠物;根据标识购买不同宠物:1.买鸟 2.买狗
//分析:类-主人类(第三方类);宠物类(父),鸟,狗(子) 方法:购买
class Pet{
public void eat() {
System.out.println("宠物在吃东西");
}
}
class Bird extends Pet{
@Override
public void eat() {
System.out.println("鸟正在吃东西");
}
}
class Dog extends Pet{
@Override
public void eat() {
System.out.println("狗正在吃东西");
}
}
class Master{
public Pet bug(int type) {
if(type==1) {
return new Bird();
}else if(type==2) {
return new Dog();
}else {
return null;
}
}
}
public class Test1 {
public static void main(String[] args) {
Master master = new Master();
Pet pet = master.bug(1); //返回子类对象;用父类Pet接收,形成多态
pet.eat(); //调子类重写方法
}
}
三、引用类型转换(扩展)
在基本类型中有类型转换:转的前提是类型要兼容;然后低类型可转高类型,高类型也能转低类型(强转)
在引用类型中,一样有类型转换:转换的前提是必须建立关系:父子关系(接口也可转)
在引用类型的转换中,有向上转和向下转两种
基本转换
-
向上转—子类转父类,默认转(多态核心)
-
向下转—父类转子类,强转
//父类Animal与子类Dog之间的相互转换:
class Animal{
}
class Dog extends Animal{
}
public class Test1 {
public static void main(String[] args) {
Animal animal = new Dog(); //向上转-默认转(多态核心)
Dog dog = (Dog)animal; //向下转-强转
}
}
强转隐患及处理
//父类与子类之间的转换:
class Pet{ //父类
}
class Bird extends Pet{ //子类
}
class Pig extends Pet{ //子类
}
public class Test2 {
public static void main(String[] args) {
Pet pet = new Bird();
//类型转换异常:Bird cannot be cast to Pig 鸟不能转成猪
//Pig pig = (Pig) pet; //向下转-强转(出现隐患)
//可以使用instanceof处理类型转换的异常 instanceof-做判断的关键字
// if (args instanceof type) { args: 变量 type:类型
if (pet instanceof Pig) {
Pig pig = (Pig) pet;
System.out.println("pet的本质类型是Pig");
}else if(pet instanceof Bird) {
Bird bird = (Bird)pet;
System.out.println("pet的本质类型是Bird");
}
}
}
扩展: instanceof
使用判定该对象是否属于该类(本类或子类)----- 返回true或false
语法:
对象 instanceof 类
结论:instanceof
不是真实处理问题,而是规避异常
四、抽象类(扩展)
某些类具有某种方法,但不能具体实现,这样的方法叫抽象方法;所在的类就是抽象类。
例如:动物类(抽象类)----叫(抽象方法)
这样的抽象类我们无法具体化(不能实例化对象),可以交给子类具体化以及具体实现。
好处:
更自然地使用多态; 抽象方法可以当成模板
来使用
抽象类细节说明:
1.抽象类能否具体化 不能
2.抽象类能否有构造方法 因为子类构造方法可以调:super()
3.有抽象方法的类一定是抽象类吗? 是的
4.抽象类是否必须有抽象方法? 可以没有抽象方法
5.抽象类是否可以有非抽象方法? 可以有
6.父类为抽象类,子类是否必须要重写抽象方法? 不是必须的,也可以将子类变为抽象类
报错:
The type Dog must implement the inherited abstract method Animal.bark()
报错处理方案:1.重写父类方法 2.把自身变抽象
直接引用多态
语法:
父类 自定义对象名 = new 子类();
//案例:动物类有一个叫的方法,可以交给狗类来完成
abstract class Animal{ //抽象类
public abstract void bark(); //抽象方法--模板
public Animal() {} //可以有构造方法
public void eat() { //非抽象方法
}
}
/*abstract*/ class Dog extends Animal{
@Override
public void bark() {
System.out.println("大黄正在汪汪汪的叫...");
}
public void run() {
System.out.println("大黄正在跑....");
}
}
public class Test1 {
public static void main(String[] args) {
//Animal animal = new Animal(); //抽象类是不能具体化的
Animal animal = new Dog(); //抽象类就是为多态而生 抽象类直接引用多态
animal.bark(); //抽象类指向子类对象,可调用子类重写方法
//animal.run(); //不可以调子类独有的方法
}
}
结论: 后续使用继承的多态,基本都是抽象类实现多态;因为应用更明确,且父类方法不会太浪费
传参多态
语法:
自定义方法名(父类 自定义对象名)
//案例:主人喂养宠物,猫,猪都是宠物,都有各自吃的行为
//分析-宠物类(父),猫类(子),猪类(子),主人类(第三方类); 方法-吃,喂养:
abstract class Pet{ //抽象类
public abstract void eat(); //抽象方法
}
class Cat extends Pet{
@Override
public void eat() {
System.out.println("加菲猫正在吃...");
}
}
class Pig extends Pet{
@Override
public void eat() {
System.out.println("佩奇正在吃...");
}
}
class Master{
public void feed(Pet pet) { //传参多态
pet.eat();
}
}
public class Test2 {
public static void main(String[] args) {
Master master = new Master();
master.feed(new Cat()); //主人喂猫
master.feed(new Pig()); //主人喂猪
}
}
返回值多态
语法参考模块:
public 父类 自定义方法名(参数列表) {
if(判定条件1) {
return new 子类1();
}else if(判定条件2) {
return new 子类2();
}else {
return null;
}
}
//返回值多态:调用方法返回子类对象,由父类引用接收;构成返回值多态
//案例:小孩购买玩具;根据标识购买不同玩具:1.买鸟 2.买鱼
//分析:类-小孩类(第三方类);玩具类(父),鸟,鱼(子) 方法:购买
abstract class Toy{
public abstract void start();
}
class Bird extends Toy{
@Override
public void start() {
System.out.println("玩具鸟正在发射..");
}
}
class Fish extends Toy{
@Override
public void start() {
System.out.println("玩具鱼正在发射..");
}
}
class Boy{
public Toy buy(int type) {
if(type==1) {
return new Bird();
}else if(type==3) {
return new Fish();
}else {
return null;
}
}
}
public class Test3 {
public static void main(String[] args) {
Boy boy = new Boy();
Toy toy = boy.buy(1);
toy.start();
}
}
扩展知识
-
子类对象创建时,父类的属性与方法会放在堆空间的哪?
答:
创建子类的对象时,父类的属性和方法也存储在堆内存中。子类对象在堆中有自己的内存空间,其中包括自己的属性和方法的内存,以及从父类继承的属性和方式的内存。这允许子类对象访问和使用父类的属性和方法。
-
调用构造方法做了哪些事?
答:开辟空间、给属性赋值、调用构造方法实现、赋值给引用变量