Java系列9—封装、继承和多态

封装、继承和多态

一、封装

(1)封装的好处

/*
 * 用户测试类
 * 对象可以随便修改属性 
 * 
 * 
 * 封装的好处:
 * 		1、封装之后,看不到事物复杂的一面,对外提供简单的入口。
 * 		
 * 		2、封装之后才会形成真正的“对象”,真正的独立体
 * 
 * 		3、封装就意味着程序可以重复使用,并且事物的适应性比较强,在任何场所都可以用
 * 
 * 		4、事物封装之后,安全性提升。
 */

public class UserTest {
	public static void main(String[] args) {
	
		//创建对象
		User user = new User();
		
		//  访问age
		System.out.println("用户年龄为:"+ user.age);
		
		// 修改用户年齡
		user.age = 20;
		System.out.println("用户年龄为:"+ user.age);
		
		// age属性暴露出来,导致数据出现不合适的数据
		// 不建议这样,建议User类型进行封装,建议在外部程序不能随意修改
		user.age = -100;
		System.out.println("用户年龄为:"+user.age);	
	}
}

class User {

	int age;

}

(2)封装的步骤


/*
 * 封装的步骤:
 * 		1、所有属性私有化,使用private关键字进行修饰,private表示私有的,修饰的所有数据只能在本类中访问。
 * 		
 * 		2、对外提供简单的操作入口,以后再想访问只能通过这些入口进行访问:
 * 			-- 对外提供两个公开的方法,分别是set方法和get方法
 * 			-- 想修改age属性,调用set方法
 * 			-- 想读取age属性,调用get方法
 * 		
 * 		3、set方法的命名规范:
 * 			public void setAge(int a){
 * 				age =a;
 * 			}
 * 			public 返回值类型   getAge(){
 * 				return age;
 * 			}
 * 
 * 需要熟记:
 * 		setter and getter方法没有static关键字
 * 		有static关键字修饰的方法调用:类名.方法名(实参)
 * 		没有static关键字修饰的方法调用:引用.方法名(实参)
 * 
 * */
public class User {
	
	// 属性私有化
	private int age;
	
	// 形参名称不能跟属性名相同
	public void setAge(int a) {
		
		// 在此处对传的参数做限制
		if (a<0 || a>150) {
			System.out.println("您输入的年龄不合法");
			return;
		}
		age = a;
	}
	
	public int getAge() {
		return age;
	}
		
}

二、继承

(1)类的继承

/**
 * 	关于java语言中的继承:
 * 		1、继承是面向对象的三大特征之一,三大特征分别是:封装、继承、多态
 * 
 * 		2、继承的基本作用是:代码复用。继承最重要的作用是:有了继承之后才有了以后的方法覆盖和多态机制。
 * 		
 * 		3、继承语法格式
 * 			[修饰符列表] class 类名 extends 父类名{
 * 				类体 = 属性  +  方法
 * 			}
 * 
 * 		4、java语言中只支持单继承,一个类中不能同时继承很多类,只能继承一个类
 * 
 * 		5、关于继承的一些术语:
 * 			B类继承A类,其中:
 * 				A类称为:父类、基类、超类、superclass
 * 				B类称为:子类、派生类、subclass
 * 
 * 		6、在java语言中,子类继承父类都继承哪些数据呢?
 * 			① 私有的不支持继承
 * 			② 构造方法不支持继承
 * 			③ 其他数据都可以继承
 * 
 * 		7、虽然java语句中只支持单继承,但是一个类也可以间接继承其他类,例如:
 * 			C extends B{
 * 			}
 * 			B extends A{
 * 			}
 * 			A extends T{
 * 			}
 * 			C直接继承B,但是也间接继承T、A类
 * 
 * 		8、java语言中假设一个类没有显示的继承任何类,该类默认集成JavaSE库中的java.lang,Object类
 *		java语言中任何一个类都有Object类的特征
 * 
 * */ 

public class ExtendsTest {
	public static void main(String[] args) {
		
	
		ExtendsTest et = new ExtendsTest();
		String s = et.toString();
		System.out.println(s);
		
		CreditAccount act = new CreditAccount();
		act.setActno("act-01");
		System.out.println(act.getActno());
	}
}

package test12;

// 银行账户
public class Account {

}

package test12;

// 信用账户
public class CreditAccount extends Account {
	
}

面试题:在java中定义一个不做事且没有参数的构造方法的作用?

Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类中加一个不做事且没有参数的构造方法。

(2)方法的覆盖/重写

区别点 重载方法 重写方法
发生范围 同一个类 子类 中
参数列表 必须修改 一定不能修改
返回类型 可修改 一定不能修改
异常 可修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问修饰符 可修改 一定不能做更严格的限制(可以降低限制)
发生阶段 编译期 运行期
package test13;
/**
 * 
 * 	关于方法的覆盖,
 * 		1、方法覆盖又被称为方法重写,英语单词:override[官方的]/overwrite
 * 
 * 		2、什么时候使用方法覆盖?
 * 			当父类的中的方法已经无法满足当前子类的业务需求。
 * 			子类有必要将父类继承过来的方法进行重新编写。
 * 			这个子类中重新编写的方法称为方法重写/覆盖。
 * 
 * 		3、什么时候发生方法重写?
 * 			方法重写发生在有继承关系的父子类之间
 * 			方法重写的时候:方法名相同,返回值类型相同,形参列表相同
 * 			方法重写的时候:访问权限不能更低,只能更高public/private/proteced	
 * 			方法重写的时候:抛出异常不能更多,可以更少		
 * 
 * 		4、建议方法重写的时候直接复制粘贴,避免出错!!!	
 * 
 * 		5、注意:
 * 			私有方法不能继承,所以不能覆盖
 * 			构造方法不能继承,所以不能覆盖
 * 			静态方法不存在覆盖
 * 			覆盖只针对方法,不谈属性
 * 
 * */
public class OverrideTest01 {
	
	public static void main(String[] args) {
		
		Animal a = new Animal();
		a.move();
		
		Cat c = new Cat();
		c.move();
		
		Bird b = new Bird();
		b.move();
		
		YingWu yw =new YingWu();
		yw.move();
	}
}

package test13;

public class Animal {
	
	public void move() {
		System.out.println("动物在移动");
	}
}

package test13;

public class Bird extends Animal{
	public void move() {
		System.out.println("鸟儿在飞翔");
	}
}

三、多态

  1. 多态的基础语法

    多态主要指编译形态和运行形态的两种形态,主要涉及类型之间的转换:

    • 向上转型(upcasting):

      父类型引用子类型对象

    • 向下转型(downcasting)

      只有访问子类中的特有的方法

    package com.bjpower.javase.test001;
    /** 
     * 	关于java语言中的多态
     * 		1、Animal、Cat、Bird三个类之间的关系
     * 			Animal是父类
     * 			Cat和Bird都继承Animal类
     * 			Cat和Bird都重写父类的move方法
     * 		
     * 		2、面向对象三大特征分别为:封装、继承、多态
     * 				
     * 		3、关于多态中设计到的概念:
     * 			—— 向上转型 (upcasting)
     * 				子类型 --> 父类型
     * 				又被称为:自动类型转换
     * 
     * 			—— 向下转型 (downcasting)
     * 				父类型——> 子类型
     * 				又被称为:强制类型转换【需要加强制类型转换符】				
     * 
     * 			—— 需要记忆:不论是向上转型,还是向下转型,两种类型之间必须要有继承关系
     * 				没有继承关系,程序无法编译通过。
     * 
     * */
    public class Test {
    	
    	public static void main(String[] args) {
    		
    		Animal a1 = new Animal();
    		a1.move();
    		
    		Cat c = new Cat();
    		c.move();
    		
    		Bird b1 = new Bird();
    		b1.move();
    		
    		// 使用多态语法继承机制
    		
    		/**
    		 * 	1、Animal和Cat之间存在继承关系,Animal是父类,Cat是子类
    		 * 
    		 * 	2、Cat is a Animal
    		 * 	
    		 * 	3、new Cat()创建的对象的类型是Cat,a2这个引用的数据类型是Animal,可见他们进行了类型转换
    		 * 		子类型转换成父类型,称为向上转型/upcasting,或者成为自动类型转换
    		 * 	
    		 * 	4、Java中允许这种语法:父类型引用指向子类型对象	
    		 * 
    		 * 
    		 * */
    		
    		Animal a2 = new Cat();
    		
    		/**
    		 * 1、java程序永远都分为编译阶段和运行阶段
    		 * 
    		 * 2、先分析编译,再分析运行阶段,编译无法通过,根本无法运行
    		 * 
    		 * 3、编译阶段编译器检查a2这个引用的数据类型为Animal,由于Animal.class字节码中有move()方法,
    		 * 	所以编译编译通过了。这个过程称为静态绑定、编译阶段绑定。
    		 * 	只有静态绑定成功之后才有后续的运行
    		 * 
    		 * 4、在程序运行阶段,JVM虚拟堆内存中真实创建的对象是Cat对象,
    		 * 	那么以下程序在运行阶段一定会调用Cat对象的move()方法,
    		 * 	此时发生了程序的动态绑定,运行阶段绑定
    		 * 
    		 * 5、无论是Cat类有没有重写move方法,运行阶段一定调用的是Cat对象的move方法,
    		 * 	因为底层真实对象是Cat对象。
    		 * 
    		 * 6、父类型引用指向子类型对象这种机制导致程序存在编译阶段绑定和运行阶段绑定两种不同的形态。
    		 * 	这种机制可以称为一种多态语法机制。
    		 * 
    		 * */
    		
    		// 只有子类和父类都有的方法才能调用
    		a2.move();
    		
    		// 编译阶段没有在Animal中找到catchMouse()方法,导致静态绑定失败,没有绑定成功
    		//  a2.catchMouse(); // 编译报错,因为catchMouse在父类中没有
    		
    		
    		/**
    		 * 需求:想让以上对象执行catchMouse方法
    		 * 	将a2从Animal类型强制类型转换为Cat类型,称为向下转型(downcasting)
    		 * 
    		 * 
    		 * 注:强制类型转换需要加强制类型转换符
    		 * 
    		 * 什么时候需要向下转型?
    		 * 		当调用的方法是子类型中特有的,在父类型中不存在,必须向下转型
    		 * */
    //		Cat a3 = (Cat)a2;  // 强制类型转换
    //		a3.catchMouse();
    		
    		// long l = 100L;
    		// int i = (int)l;
    		
    		
    		
    		/**
    		 * 1、以下程序编译是没有问题的,因为编译器检查到a3引用的数据类型是Animal
    		 * Animal和Cat存在继承关系,并且Animal是父类型,Cat是子类型
    		   *     父类型转换成子类型叫做向下转型,语法合格
    		 *       
    		 * 2、程序虽然编译通过了,但是程序在运行阶段会出现异常,因为JVM堆内存中真实存在的是Bird类型,
    		 *  Bird类型 无法转换成Cat类型,因为两种类型之间不存在任何继承关系,此时会出现异常:
    		 *  	 java.lang.ClassCastException
    		 *  	类型转换异常,这种异常总是发生在"向下转型"时
    		 * */
    		Animal a3 = new Bird();
    //		Cat c3 = (Cat)a3;
    		
    		/**
    		 * 1、以上异常只有在强制类型转换时候发生,也就是"向下转型"时存在隐患(编译过了,运行报错)
    		 * 
    		 * 2、向上转型只要编译通过,运行一定不会出现问题,Animal a = new Cat();
    		 * 
    		 * 3、向下转型编译通过,运行可能出现错误;Animal a3 = new Bird(); Cat c3 = (Cat)a3;
    		 * 
    		 * 4、向下转型怎么避免java.lang.ClassCastException异常?
    		 * 		使用instanceof运算符
    		 * 
    		 * 5、instanceof运算符怎么使用?
    		 * 		5.1、语法格式
    		 * 			(引用 instanceof 数据类型名)
    		 * 		5.2、以上运算符的执行结果类型是布尔类型,结果可能是true/false
    		 * 		5.3、关于运算结果true/false
    		 * 			假设:(a instanceof Animal)
    		 * 			true表示:
    		 * 				a这个引用执行的是Animal类型
    		 * 			false表示:
    		 * 				a这个引用不是一个Animal类型
    		 * 
    		 * 6、Java语法规范中要求:在进行强制类型转换之前,建议采用instanceof运算符进行判断,
    		 * 	避免ClassCastException
    		 * 
    		 * */
    		if(a3 instanceof Cat) { // a3是一个Cat类型的对象
    			Cat c3 = (Cat)a3;
    			c3.move();
    		}else if(a3 instanceof Bird) { // a3是一个Bird类型的对象
    			Bird c3 = (Bird)a3;
    			c3.move();
    		};
    		
    	}
    }
    
    package com.bjpower.javase.test001;
    
    
    // 动物类
    public class Animal {
    
    	public void move() {
    		System.out.println("动物在移动!");
    	}
    
    }
    
    
    package com.bjpower.javase.test001;
    
    
    // 猫类
    public class Cat extends Animal{
    	
    	
    	// 重写父类的move()方法
    	public void move() {
    		System.out.println("猫在走猫步!");
    	}
    	
    	// 猫类特有的方法
    	public void catchMouse() {
    		System.out.println("猫抓老鼠");
    	}
    	
    }
    
    
    package com.bjpower.javase.test001;
    
    
    // 鸟类
    public class Bird extends Animal{
    	
    	public void move() {
    		System.out.println("鸟儿在飞翔");
    	}
    	
    }
    
    
  2. 多态知识点总结

    package com.bjpower.javase.test001;
    
    public class Test02 {
    	
    	public static void main(String[] args) {
    		
    		// 父类型引用指向子类型对象
    		// 向上转型
    		Animal a1 = new Cat();
    		Animal b2 = new Bird();
    		
    		
    		// 向下转型
    		if(b2 instanceof Bird) {
    			Bird b3 = (Bird)b2;
    			b3.move();
    		}else if(b2 instanceof Cat) {
    			Cat b3 = (Cat)b2;
    			b3.move();
    		};
    	}
    }
    
    
  3. 多态在开发中的作用

以人喂养宠物为例,Cat和Dog和Snake都继承Pet,人在喂养时针对Pet,不关注具体传的对象是哪一个。

package com.bjpower.javase.test002;
/**
 * 多态在实际开发中的作用
 * 	1、分析:主人喂养宠物这个场景实现需要进行类型的抽象:
 * 		—— 主人【类】
 * 		—— 主人可以喂养宠物,所以主人有喂养的动作
 * 		—— 宠物【类】
 * 		—— 宠物可以吃东西,所以宠物有吃东西这个动作
 * 	2、面向对象编程的核心,定义好类,然后将实例化为对象,给一个环境驱使一下,让各个对象之间形成一个系统
 * 
 *  3、多态的作用是什么?
 *  	降低程序的耦合度,提高程序的能力
 *  	能使用多态尽量使用多态
 *  	父类型的引用指向子类型
 *  
 *  核心:面向抽象编程,尽量不要面向具体编程
 * 
 * */

public class Test {
	
	public static void main(String[] args) {
		Master zhangsan = new Master();
		Cat tom = new Cat();
		Dog erHa = new Dog();
		Snake snake = new Snake();
		zhangsan.feed(tom);
		zhangsan.feed(erHa);
		zhangsan.feed(snake);
		
	}
}

package com.bjpower.javase.test002;

/**
 *  人类
 * */

// 降低程序的耦合度【解耦合】,提高程序的扩展能力【软件开发的一个很重要的目标】
public class Master {
	
	// 提倡:面向对象编程,不要面向具体编程
	public void feed(Pet c) {
		c.eat();
	}
}

宠物类:

package com.bjpower.javase.test002;

public class Pet {
	public void eat() {
		System.out.println("吃");
	}
}

package com.bjpower.javase.test002;

/**
 * 	宠物小猫
 * */
public class Cat extends Pet{
	
	// 小猫爱吃鱼
	public void eat() {
		System.out.println("小猫在吃鱼");
	}
}

package com.bjpower.javase.test002;
/**
 *  宠物小狗
 * */
public class Dog extends Pet{

	public void eat() {
		System.out.println("小狗吃骨头");
	}
}

package com.bjpower.javase.test002;

/**
 * 蟒蛇宠物
 * */
public class Snake extends Pet{
	public void eat() {
		System.out.println("蛇吞象");
	}
}

猜你喜欢

转载自blog.csdn.net/qq_42486675/article/details/106508107