封装、继承、多态

本文讲解了封装理论,包括属性私有化和set/get方法,以及继承的概念、方法覆盖/重写和多态的实现,向上转型与向下转型,以实例演示了如何利用多态在喂养宠物场景中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、封装

理论

先看下面的代码

public class Test{
	public static void main(String[] args){
		User u = new User();
		u.age = -100;
		u.age = -400;
	}
}
class User{
	int age;
}

对于当前程序来说:User类中的age属性在外部程序中可以随意访问,导致age属性的不安全。

一个User对象表示一个用户,用户的年龄不可能为负数,上面程序当中年龄值为负数,程序运行的时候并没有报错,这是当前程序存在的缺陷。所以需要封装机制。

对于封装而言,一个对象它所封装的是自己的属性和方法。封装的的好处:

  1. 封装之后,对于那个事物来说,看不到这个事物比较复杂的那一面,只能看到该事物简单的那一面。复杂性封装,对外提供简单的操作入口。
    照相机就是一个很好的封装的案例,照相机的实现原理非常复杂,但是对于使用照相机的人来说,操作起来是非常方便的是非常便捷的。还有像电视机也是封装的,电视机内存实现非常复杂,但是对于使用者来说不需要关心内部的实现原理,只需要会操作遥控器就行。
  2. 封装之后才会形成真正的“对象”,真正的“独立体”
  3. 封装就意味着以后的程序可以重复使用。并且这个事物应该适应性比较强,在任何场合都可以使用。
  4. 封装之后,对于事物本身,提高了安全性。【安全级别高】

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法

如果不想被外界方法,我们大可不必提供方法给外界访问。

步骤

  1. 所有属性私有化,使用private关键字进行修饰, private表示私有的,修饰的所有数据只能在本类中访问。
  2. 对外提供简单的操作入口,也就是说以后外部程序要想访问age属性,必须通过这些简单的入口进行访问
  3. 对外提供两个公开的方法,即set、get方法
    1. 想修改age属性,调用set方法
    2. 想读取age属性,调用get方法
  4. set方法的命名规范:
    1. public void setAge(int a) {age = a;}
      
  5. get方法的命名规范:
    1. public int getAge (){return age;}
      

注意:

set、get方法没有static关键字

static关键字修饰的方法调用:类名.方法名(实参) ;

没有static关键字修饰的方法调用:先创建对象和引用、引用.方法名(实参) ;

public class Test{
	public static void main(String[] args){
		User u = new User();
		
		//报错。因为私有化属性不可以直接访问
		System.out.println(u.age);
		
		//通过set、get方法修改和访问
		u.setAge( -100 );
		System.out.println( u.getAge );
	}
}

class User{
	//属性私有化
	private int age;
	
	//set方法,没有返回值,只负责修改数据
	pubilc void setAge(int a){
		age = a;
	}
	
	//get方法
	public int getAge(){
		return age;
	}
}

操作入口变成了 set、get 方法,在set、get 方法执行过程中,进行安全过滤

可以设定修改数值的条件

class User{
	//属性私有化
	private int age;
	
	//set方法
	pubilc void setAge(int a){
		if( a < 0 || a > 150){
			System.out.println("您输入的年龄有误");
			return;
		}
		age = a;
	}
	
	//get方法
	public int getAge(){
		return age;
	}
}
class person{
	private String sexName;
	
	public String getSexName() {  
		if("0".equals(sex)){  
			sexName = "女";  
		}else if("1".equals(sex)){  
			sexName = "男";  
		}else{  
			sexName = "人妖";  
		}
		return sexName;  
	}  
}

二、继承

继承

语法格式:[修饰符列表] class 类名 extends 父类名{ 类体; }

  1. Java中是单继承,一个类只可以继承一个类
  2. B类 extends A类:
    1. B类:子类、派生类、 subclass
    2. A类:父类、基类、超类、surperclass
  3. 私有的不可以继承,构造方法不可以继承。其他均可以继承
  4. 虽然只有单继承,但是可以间接继承
  5. 若一个类没有显示 extends某个类,则默认继承Object类

java中任何一个类都有Object类特征

这里的tt.toString()方法就是Object类中继承过来使用的

public class Test{
	public static void main(String[] args){
		Test tt = new Test();
		String s = tt.toString();
		System.out.println(s);
	}
}

编写子类父类

public class Test{
	public static void main(String[] args){
		Animal a = new Animal();
		a.move();
		Cat c = new Cat();
		c.move();
		Bird b = new Bird();
		b.move();
	}
}
class Animal{
	public void move(){
		System.out.println("动物行走");
	}
}
class Cat extends Animal{}
class Bird extends Animal{}

这里的set / get方法不是私有的,所以子类可以调用父类方法

public class Test{
	public static void main(String[] args){
		B bb = new B();
		bb.setAge(20);
		bb.setName("草莓");
		System.out.println(bb.getAge+","+bb.getName);
	}
}
class A{
	private int age;
	String name;
	public A(){}
	public A(int age,String name){
		this.age = age;
		this.name = name;
	}
	public int getAge(){return age;}
	public void setAge(int age){this.age = age;}
	public String getName(){return name;}
	public void setName(String name){this.name = name;}
}
class B extends A{
	private int id;
	public B(){
		super();
	}
	public int getId(){return id;}
	public void setId(int id){this.id = id;}
}

方法覆盖/重写 Override

方法重载 Overloard

  1. 什么么情况下使用?
    当同一个类中,方法功能是相似的,建议使用方法重载
  2. 需要满足什么条件?
    在同一个类中,方法名相同,参数列表不同:类型、顺序、数量
  3. 方法重载和什么无关?
    和方法返回值、方法的修饰符列表无关

方法覆盖 Override

  1. 什么情况使用?
    当父类中的方法无法满足子类的需求,子类需要将父类的方法进行升级改造
  2. 子类重写方法的方法名、形参列表和父类被重写方法一致
  3. 子类重写方法的权限修饰符大于等于父类被重写方法
  4. 子类不能重写父类中private修饰的方法
  5. 返回值类型
public class Test{
	public static void main(String[] args){
		Animal a = new Animal();
		a.move();
		Cat c = new Cat();
		c.move();
		Bird b = new Bird();
		b.move();
	}
}
class Animal{
	//假设这边还有其他一百多行代码
	public void move(){
		System.out.println("动物行走");
	}
}
class Cat extends Animal{
	public void move(){
		System.out.println("猫走猫步");
	}
}
class Bird extends Animal{
	public void move(){
		System.out.println("鸟在飞");
	}
}

三、多态

多态涉及到的两个概念:

  • 向上转型
    子类(多) ====> 父类(少) (自动型)
  • 向下转型
    父类(少) ====> 子类(多) (强制型,需要加强制类型转换符)

**注意:**无论是向上还是向下,两个类型之间要有继承关系,没有继承关系是无法转型的

向上转型

【父类引用指向子类对象】

//父类,动物类
class Animal {
	protected void run() { 
		System.out.println("动物在移动!!!");
	}
}

//子类,猫类
class Cat extends Animal {
	//重写父类方法
	public void run() {
		System.out.println("猫在走猫步!!!");
	}
	//子类特有的方法,猫会抓老鼠
	public void catchMouse() {
		System.out.println("猫正在抓老鼠!!!");
	}
}
//子类,鸟儿类
class Bird extends Animal {
	//重写父类方法
	public void run() {
		System.out.println("鸟儿在飞翔!!!");
	}
	//子类特有的方法,鸟正在飞
	public void fly() {
		System.out.println("鸟正在飞!!!");
	}
}

使用多态语法机制

new Cat()创建的对象是Cat类型,子类类型,a1这个引用是Animal类型,父类类型

子类类型转换为父类类型,称为自动类型转换,向上转型 / upcasting

父类引用指向子类对象

public class Test{
	public static void main(String[] args){
		//使用多态语法机制
		Animal a1 = new Cat();
		a1.run();
	}
}

输出结果:猫在走猫步!!!
请添加图片描述

理论:

java程序分为编译阶段和运行阶段

先编译,再运行

编译阶段:编译器知道a1这个引用是Animal类型,在编译检查a1.run()的语法时,会去引用a1Animal.class中寻找run()方法,成功找到,编译通过。

该阶段称为 “ 静态绑定 ” ,“ 编译阶段绑定 ”

运行阶段:JVM 堆内存当中真是创建的对象是Cat对象,程序在执行阶段a1.run()调用的是Cat对象内部的run()方法

该阶段称为 “ 动态绑定 ” ,“ 运行阶段绑定 ”

无论子类是否重写父类方法,运行阶段调用的一定是子类中的方法。就算未重写,也已经继承了

注意以下代码: 程序为什么不通过?

编译阶段:引用a1的数据类型是Animal类型,在Animal.class中寻找不到catchMouse()方法,所以编译不通过

//编译错误
public class Test{
	public static void main(String[] args){
		Animal a1 = new Cat();
		a1.catchMouse();
	}
}

程序为什么不通过?

因为二者之间没有继承关系,无法进行转换

Bird b = new Cat();

向下转型

向上转型可以调用子类中和父类同名的方法。当调用的方法是子类中特有的,在父类当中不存在时,就需要向下转型、

语法结构:子类类型 新引用 = (子类类型) 父类引用;

a1是无法直接调用catchMouse()方法的,因为Animal.class中没有该方法

public class Test{
	public static void main(String[] args){
		Animal a1 = new Cat();
		a1.run();
		Cat c1 = (Cat)a1;
		c1.catchMouse();
	}
}

注意以下代码: 程序为什么不通过?

编译通过,因为a2的数据类型是Animal类型,与Cat类型之间存在继承关系,可以转型。c2的数据类型是Cat类型,对象中存在catchMouse()方法,静态绑定成功,编译通过。

运行不通过。报错:ClassCastException 类型转换异常

因为JVM堆内存中真实存储的对象是Bird类型,Bird对象无法转换成Cat对象,因为二者之间不存在继承关系

//运行报错
	Animal a2 = new Bird();
	Cat c2 = (Cat)a2;
	c2.catchMouse();

instanceof运算符

为了避免上面的异常发生,就有了instanceof运算符

语法格式:引用 instanceof 数据类型名

即判断前面的引用指向的对象是不是后面的数据类型

所以上面的代码应该写成如下:

public class Test{
	public static void main(String[] args){
		Animal a2 = new Bird();
		a2.run();
		if(a2 instanceof Cat){
			Cat c2 = (Cat)a2;
			c2.catchMouse();
		}else if(a2 instanceof Bird){
			Bird b2 = (Bird)a2;
			b2.fly();
		}
	}
}

例题理解多态的作用

注意: 方法传递数据的时候传递的是具体的值,所以下面例题中,调用主任方法时,传递的是具体对象

	pipi.feed(new Cat());

下面给道例题理解一下多态在开发中的作用:

/*
 * 编写程序模拟主人喂养宠物的场景:
 * 	提示一:
 * 		主人类:Master
 * 		宠物类:Pet
 * 		宠物子类:Dog,Cat,Bird
 * 	提示二:
 * 		主人应该有喂养方法:feed()
 * 		宠物应该有吃的方法:eat()
 * 		只要主人喂养,宠物就吃。
 * 
 * 要求:主人类中只提供一个喂养方法feed(),要求达到可以喂养各种类型的宠物。
 * 	编写测试程序:创建主人对象
 * 			创建各种宠物对象
 * 			调用主人的喂养方法feed(),喂养不同的宠物,观察执行结果。
 * 	通过改案例,理解多态在开发中的作用。
 * 	重要提示:feed()方法是否需要一个参数,参数选什么类型??? 
 */

public class MyTest{
	public static void main(String[] args) {
		//实例化主人类
		Master pipi = new Master();
		//实例化三种宠物类
		Dog d1 = new Dog();
		Cat c1 = new Cat();
		Bird b1 = new Bird();
		//主人调用自己的喂养方法feed()来实现不同宠物的吃方法eat()
		pipi.feed(d1);
		pipi.feed(c1);
		pipi.feed(b1);
	}
}

//主人类
class Master {
	//feed()方法
	public void feed(Pet pet) {
		pet.eat();
	}
}

//宠物类
class Pet {
	//eat()方法
	public void eat() {
		System.out.println("宠物" + "在吃东西!!!");
	}
}

//宠物子类,Dog类
class Dog extends Pet {
	//重写eat()方法
	public void eat() {
		System.out.println("狗狗" + "喜欢啃骨头!!!");
	}
}

//宠物子类,Cat类
class Cat extends Pet {
	//重写eat()方法
	public void eat() {
		System.out.println("猫儿" + "喜欢吃鱼!!!");
	}
}

//宠物子类,Bird类
class Bird extends Pet {
	//重写eat()方法
	public void eat() {
		System.out.println("鸟儿" + "喜欢吃虫子!!!");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值