面对对象的三大特征(封装、继承、多态)(全基础详解)
文章目录
一、封装
对于当前程序来说:user类中可以随意访问,导致age属性的不安全。
一个user对象表示一个用户,用户的年龄不可能成为负数,以下程序当中年龄值为负数,程序运行的时候并没有报错,这是大部分全部程序存在的缺陷。
public class UserTest {
public static void main(String[] args) {
//创建用户对象
User user = new User();
//访问age
//读取年龄值[get]
System.out.println("该用户年龄:"+user.age);
//修改年龄值【set】
user.age = 20;
//读取年龄值【get】
//myeclipse快捷键:ctrl+shift+f【对齐】
System.out.println("用户年龄:" + user.age);
//修改年龄值
//这里的age属性显然完全暴露给外部程序的,对于程序员来说可以操作user对象当中所有的细节,导致user中部分数据不安全
//不建议这样,建议user类进行封装,建议在外部程序中不能随意访问user对象当中的属性。这样可以保证属性的安全。
user.age = -100;
//读取年龄值
System.out.println("用户年龄:" + user.age);
}
}
public class User {
//年龄
int age;
}
封装的好处:
- 封装之后,对于拿个事物来说,看不到这个事物比较复杂的那一面,只能看到该事物简单的那一面
复杂性封装,对外提供简单的操作入口,照相机就是很好的封装的案例。照相机的实现原理非常复杂,
但是对于使用者来说操作起来是非常方便的,还有电视机。 - 封装之后从才会形成真正的”对象“,真正的”独立体“
- 封装就意味着以后的程序可以重复使用。并且这个事物应该适应性比较强,在任何场合都可以使用。
- 封装之后,对于事物本身,提高了安全性。【安全级别提高】
封装的步骤:
-
所有属性私有化,使用private关键字进行修改,private表示私有的,修饰的所有的数据只能在本类中访问。
-
对外提供简单的操作入口,也就是说以后外部程序要想访问age属性,必须通过这些简单的入口进行访问。
-对外提供两个公开的方法,分别是set方法和get方法
-想修改age属性,调用set方法;
-想读取age属性,调用get方法;
一个属性提供俩方法,两个提供四个
- set方法的命名规范:
p u b l i c v o i d s e t + 属 性 首 字 母 大 写 ( 形 参 ) public void set+属性首字母大写(形参){ } publicvoidset+属性首字母大写(形参)
- get方法的命名规范:
p u b l i c v o i d g e t + 属 性 首 字 母 大 写 ( ) r e t u r n + a g e ; public void get+属性首字母大写(){ return+age; } publicvoidget+属性首字母大写()return+age;
属性访问时访问形式:
-第一种方式:像读取这个属性的值,读取get;
-第二种方式:想修改这个属性的值:修改set:
注:需要背会
setter and getter方法没有static关键字
有static关键字修饰的方法怎么调用:类名.方法名{实参}
没有static关键字修饰的方法怎么调用:引用.方法名{实参}
public class User {
private int age;
//set 方法没有返回值,因为set方法只负责修改数据
/*public void setAge(int age){
age = age;//java有就近原则,这里其实并没有给age属性赋值,这里的age都是局部变量age
*/
public void setAge(int a){
//编写业务逻辑代码进行安全控制
//age = a;
if(a<0||a>150){
System.out.println("对不起,您提供的年龄不合法:");
return ;
}
}
//getAge
public int getAge(){
return age;
}
}
public static void main(String[] args) {
//创建User对象
User user = new User();
//编译报错,age属性私有化,在外部无法直接访问
/*从此之后age属性变得非常的安全,但是有点太安全了。
对于目前的程序来说,age属性彻底在外部访问不到了
System.out.println(user.age);*/
//修改
user.setAge(-100);
//读取
System.out.println(user.getAge());
}
私有的属性不能在外部直接访问,这就是封装操作入口编程了只能通过set和get方法进行访问在set和get方法执行过程中可以进行安全过滤
public class test03use {
public static void main(String[] args) {
test03 c = new test03();
/*私有的属性不能在外部直接访问,这就是封装
操作入口编程了只能通过set和get方法进行访问
在set和get方法执行过程中可以进行安全过滤*/
c.setId(100);
c.setAge(50);
c.setName("laowang");
c.setAddr("上海");
System.out.println(c.getId());
System.out.println(c.getAge());
System.out.println(c.getName());
System.out.println(c.getAddr());
}
//顾客类
public class test03 {
//属性
private int age;
private String name;
private int id;
private String addr;
//setter getter方法
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;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
二、继承
继承是面向对象的三大特征之一(封装、继承、多态)。
继承"基本"作用是:/
代码复用。
但是继承最重要的作用是:有了继承才有了以后的"方法的覆盖"和"多态的机制"。
继承语法格式
[ 修 饰 符 列 表 ] c l a s s 类 名 e x t e n d s 父 类 类 体 = 属 性 + 方 法 [修饰符列表] class 类名 extends 父类{ 类体 = 属性 + 方法 } [修饰符列表]class类名extends父类类体=属性+方法
Java语言当中的继承只支持单继承,一个类不能同时继承很多类,只能继承一个类,在C++中支持多继承。
关于继承中的术语
B类继承A类,其中:
A类称为:父类、基类、超类、superclass
B类称为:子类、派生类、subclass
在Java语言当中子类继承父类都继承那些数据?
-私有的不支持继承
-构造方法不支持继承
-其他数据都可以被继承
虽然Java语言当中只支持单继承,但是一个类也可以间接继承,例如:
C extends B{
}
B extends A{
}
A extends T{
}
C直接继承B类,但C类间接继承T、A类
Java语言当中假设一个类没有显示的继承任何类,该类默认继承JavaSE当中提供的java.lang.Object类.(老祖宗)。
Java语言当中任何一个类都有Object类的特征。
Ctrl+shift+t:查找类型(open type)
Ctrl+shift+r:查找资源(open Resoure)
public class ExtendsTest2 {
public static void main(String[] args) {
C c = new C();
c.dosome();//这里调用的dosome方法是B类中继承A过来的dosome方法
}
}
class A{
public void dosome()
{System.out.println("do some");}
}
class B extends A{
}
class C extends B{
}
实例代码:
创建一个Account账户类
public class Account {
private String acton;
private double balance;
public Account() {
}
public Account(String acton, double balance) {
this.acton = acton;
this.balance = balance;
}
public String getActon() {
return acton;
}
public void setActon(String acton) {
this.acton = acton;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
创建一个CreatAccount信用账户类
public class CreatAccount extends Account{
private double credit;
public CreatAccount() {
}//这里继承可以理解为把子类CreatAccount中的acton,balance代码拿过来
public double getCredit() {
return credit;
}
public void setCredit(double credit) {
this.credit = credit;
}
一个Extendtest测试类
public class ExtendsTest {
public static void main(String[] args) {
CreatAccount act =new CreatAccount();
act.setActon("act-011");
act.setBalance(-1000.0);
act.setCredit(0.99);
System.out.println(act.getActon()+act.getBalance()+act.getCredit());
}
三、多态
多态语法
Cat 继承 Animal
Bird 继承 Animal
Cat Bird 无关系
public class Animal {
public void move(){
System.out.println("动物在移动!");
}
public class Bird extends Animal{
//重写从父类中继承过来的方法
public void move(){
System.out.println("鸟儿在飞翔!");
}
//子类特有的行为/动作
public void fly(){
System.out.println("Bird Fly");
}
public class Cat extends Animal{
//重写父类中继承过来的方法
public void move(){
System.out.println("猫在走猫步!");
}
//不是从父类中继承过来的方法
//这个方法是子类中特有的行为【不是说所有猫都能抓老鼠】
public void catchMouse(){
System.out.println("猫抓老鼠!");
}
关于多态中涉及到的几个概念:
*向上转型(upcasting)
-子类型转换成父类型
又称:自动类型转换
*向下转型(downcasting)
-父类型转换成子类型
又称:强制类型转换【需加强制类型转换符】
*无论是向上转型还是向下转型之间都必须有继承关系,没有继承关系编译无法编译通过。
建立一个Test测试类:
public class Test {
public static void main(String[] args) {
//以前编写的程序
Animal a1 = new Animal();
a1.move();
Cat c1 = new Cat();
c1.move();
Bird b1=new Bird();
b1.move();
}
- Animal和Cat之间存在继承关系,Animal是父类,Cat是子类
- Cat is a Animal
- new Cat()创建的对象是Cat,a2这个引用的数据类型是Animal,可见他们进行了类型转换
子类型转换成父类型,称为向上转型/upcasting,或者称为自动类型转换 - java中允许这种语法:父类型引用指向子类型对象
Animal a2 = new Cat();//这里a2是Cat类中的
//Bird b2 = new Cat();//编译报错,因为两种类型之间不存在任何继承关系,无法向上向下转型。
a2.move();
- java程序永远都分为编译阶段和运行阶段。
- 线分析编译阶段,再分析运行阶段,编译无法通过,根本是无法运行的。
- 编译阶段编译器检查a2这个引用的数据类型为Animal,由于Animal.class字节码当中有move()方法所以编译通过了。
这个过程我们称为静态绑定,编译阶段绑定。只有静态绑定成功之后才有后续的运行。 - 在程序运行阶段,JVM堆内存当中真实创建的对象是Cat对象,那么程序在运行阶段一定会调用Cat对象的move()方法,此时发生了程序的动态绑定,运行阶段绑定。
- 无论是Cat类有没有重写move方法,运行阶段一定调用的是Cat对象的move方法,因为底层真实对象就是Cat对象。
- 父类型引用指向子类型对象这种机制导致程序存在编译阶段绑定和运行阶段绑定两种不同的形态/状态,这种机制可以成为一种多态语法机制。
a2.catchMouse();
分析以上程序为什么不能调用?
因为编译阶段编译器检查到a2的类型是Animal类型,从Animal.class字节码文件当中查找catchMouse()方法最终没有找到该方法,导致静态绑定失败,没有绑定 成功,也就是说编译失败。别谈运行了。
- 需求:
- 假设想让以上的对象执行catchMouse()方法,怎么做?
- a2是无法直接调用的,因为a2的类型Animal,Animal中没有catchMouse()方法。
- 我们可以将a2强制类型转换为Cat类型。
- a2的类型是Animal(父类),转换成Cat类型(子类),被称为向下转型/downcasting/强制类型转换。
- 注:向下转型也需要两种类型之间必须有继承关系,不然编译报错,强制类型转换要加强制类型转换符。
- 什么时候需要使用向下转型呢?
- 当调用的方法或者访问的属性是子类型中特有的,在父类型中不存在,必须进行向下转型。
Cat c2 = (Cat)a2;
c2.catchMouse();
Animal a3 = new Bird();
- 以下程序编译是没有问题的,因为编译器检查到a3的数据类型是Animal
Animal和Cat之间存在继承关系,并且Animal是父类型,Cat是子类型,父类型转换成子类型叫做向下转型,语法合格。 - 程序虽然编译通过了,但是程序在运行阶段会出现异常,因为JVM堆内存当中真是存在的对象是Bird类型, Bird对象无法转换成Cat对象,因为两种类型之间不存在继承关系,此时出现了著名的异常:
java.lang.ClassCastException
类型转换异常,这种异常总是在”向下转型“的时候会发生
Cat c3 = (Cat)a3;
-
以上一程序只有在强制类型转换的时候会发生,也就是说”向下转型“存在隐患(编译过了,运行错了!)
-
向上转型只要编译通过,运行一定不会出问题。Animal a = new Cat()
-
向下转型编译通过,运行可能错误:Animal a3 = new Bird();Cat c3 = (Cat)a3;
-
怎么避免向下转型出现ClassCastException呢?
使用instanceof运算符可以避免出现以上的异常。
instanceof运算符
-
instanceof运算符怎么用?
语法格式:
(引用instanceof)数据类型名
以上运算符执行结果类型是布尔类型,结果可能是true/false关于运算结果true/false:
假设:(a instanceof Animal)
true表示:
a这个引用指向的对象是一个Animal类型。
false表示:
a这个引用指向对象不是一个Animal类型 -
Java规范中要求:在进行强制类型转换之前,建议采用instanceof运算符进行判断,避免ClassCastException异常的发生
if(a3 instanceof Cat)
{Cat c3 = (Cat)a3;
a3.move();}
else if(a3 instanceof Cat)
{Bird b2 = (Bird)a3;
b2.fly();}
多态在实际开发中的作用
分析:主人喂养宠物这个场景
-主人【类】
-主人可以喂养宠物,所以主任有喂养这个动作
public void feed(Cat c){
c.eat();
}
public void feed(Dog c){
c.eat();
}
-宠物【类】
-宠物可以吃东西,所以宠物有吃东西这个动作
/**
* 宠物小猫
*/
public class Cat extends Pet{
public void eat(){
System.out.println("小猫爱吃鱼");
}
public class Dog extends Pet{
//喜欢吃骨头
public void eat(){
System.out.println("小狗喜欢吃骨头");
}
面向对象编程的核心:定义好类,给一个环境驱使一下,让各个对象之间写作起来形成一个系统。
public class Test {
public static void main(String[] args) {
//创建主人
Master zhangsan = new Master();
//创建猫对象
Cat tom = new Cat();
//主任喂猫
zhangsan.feed(tom);
//创建小狗对象
Dog erHa = new Dog();
//主人喂狗
zhangsan.feed(erHa);
}
这种方式没有使用java语言的多态机制,存在的缺点:master的扩展力很差,因为只要加一个新的宠物,Master类就需要添加新的方法
Master和Cat、Dog这两个类型的关联程度很强,耦合度很高,扩展里差。
降低程序的耦合度【解耦合】,提高程序的扩展李【软件开发一个很重要的目标】
Master类多态:
public class Master {
public void feed(Pet pet){//父类型引用
pet.eat();
}
创立一个宠物类(所有宠物都有吃的特征):
public class Pet {
public void eat(){
}
-
Master主人类面向的是一个抽象的Pet,不再面向具体的宠物
-
提倡:面向抽象编程,不要面向具体编程
-
面向抽象编程的好处是,耦合度低,扩展力强
多态的作用
-
降低程序的耦合度,提高程序的扩展力。
-
能使用多态尽量使用多态。
-
父类型引用指向子类型对象。
zhangsan.feed(erHa);
}
这种方式没有使用java语言的多态机制,存在的缺点:master的扩展力很差,因为只要加一个新的宠物,Master类就需要添加新的方法
Master和Cat、Dog这两个类型的关联程度很强,耦合度很高,扩展里差。
降低程序的耦合度【解耦合】,提高程序的扩展李【软件开发一个很重要的目标】
***
Master类多态:
```java
public class Master {
public void feed(Pet pet){//父类型引用
pet.eat();
}
创立一个宠物类(所有宠物都有吃的特征):
public class Pet {
public void eat(){
}
-
Master主人类面向的是一个抽象的Pet,不再面向具体的宠物
-
提倡:面向抽象编程,不要面向具体编程
-
面向抽象编程的好处是,耦合度低,扩展力强
多态的作用
-
降低程序的耦合度,提高程序的扩展力。
-
能使用多态尽量使用多态。
-
父类型引用指向子类型对象。
本文深入解析面向对象编程的三大核心特性:封装、继承与多态。详细介绍了每个特性的概念、作用及应用,包括封装如何提升数据安全性,继承如何实现代码复用,以及多态如何降低程序耦合度,提高扩展性。
1万+

被折叠的 条评论
为什么被折叠?



