封装,继承,多态
一、封装
博客封装知识点配套视频:
java中面向对象的三大特性之封装
尽可能的隐藏对象内部实现细节,控制用户的修改和访问权限,保障内部属性(成员变量)的安全
生活中的封装
不安全的设计
/** * 创建一个类,创建一个模板 */ public class Account { String no; //卡号 String password; //密码 double balance; //余额 public Account() { } public Account(String no, String password, double balance) { this.no = no; this.password = password; this.balance = balance; } }
创建对象
public class TestAccount { public static void main(String[] args) { //1. 创建对象 Account account = new Account(); //2.设置属性 account.no="171727213891278"; //非法的卡号 account.password="123456"; //非法的密码 account.balance=10000000; //非法的余额 //3.访问属性 System.out.println(account.balance); } }
出现的问题:可以随意访问属性,修改属性,破坏我们对象原有状态,设计不安全
1、private修饰符
public 公共的 private 私有的
概念:修饰程序中的各个组件,被修饰的组件拥有不同的特点与权限
例:用户去银行取钱,有知道账号,密码,余额的权限,但是没有修改余额和账号的权限,银行工作(柜台)人员,他有知道银行卡账号的权限,查看账号余额的权限,重置密码的权限
private的特点:
1. private表示私有的可以修饰属性和方法 private String no; //卡号 2. 被修饰的组件只能在本类中使用,对外不可见
例
public class MyClass { int a; private int b;//被private修饰的属性、成员变量 。只能在本类中使用 public void ma(){ System.out.println("ma"); } private void mb(){ //被private修饰的方法/函数,只能在本类中使用 System.out.println("mb"); } } class TestMyClass{ public static void main(String[] args) { MyClass mc = new MyClass(); mc.a=1; mc.b=2;//编译出错,无权限访问 mc.ma(); mc.mb();//编译出错,无权限访问 } }
2、私有化属性和方法【重点应用】
使用private修饰属性,将属性或方法私有化,控制访问权限
不合理的过度安全
1. 如果一个类所有的属性或方法都是私有的,那么该类的对象就没有存在的价值了 //比如捡到一张银行卡,就算存十个亿也和你没关系 2. 控制访问权限不是一刀切,有些情况下也是需要访问属性或修改属性的 //疫情下比如生孩子,没做核酸检测也是可以进医院的
3、提供访问或修改私有的成员的途径(get/set方法)【重点应用】
成员指属性和方法
get/set方主要解决过度安全问题,合理的控制内部属性的安全
set方法的语法
public void set属性名(形参类型 形参名){ this.属性名=形参名 } // 1. 形参类型和属性类型必须一致 // 2. 形参名与属性名一致 // 3. 方法中的属性名必须大写
get方法的语法
public 返回值类型 get属性名(){ return 属性; } //1. 返回值类型与属性类型一致 //2. 方法中的属性名必须大写
案例:
package com.tjcu.whj.account; /** * @author Wanghj * @version 1.0 * @date 2022/12/4 9:14 * 创建一个类,创建一个模板 */ public class Account { private String no; //卡号 /*set方法: public void set属性名(形参类型 形参名){ this.属性名=形参名 } 1. 形参类型和属性类型必须一致 2. 形参名与属性名一致 3. 方法中的属性名必须大写 * */ public void setNo(String no) { this.no = no; } /* public 返回值类型 get属性名(){ return 属性; } //1. 返回值类型与属性类型一致 //2. 方法中的属性名必须大写 * */ public String getNo() { return no; } } //访问属性或修改属性均改为了调用get.set方法的形式 class TestAccount { public static void main(String[] args) { //1. 创建对象 Account account = new Account(); //2.设置属性 account.setNo("123455"); //3.访问属性 System.out.println(account.getNo()); } }
4、get、set方法的实际作用
赋值前需要先验证数据的合法性
private String no; //卡号 private String password; //密码 private double balance; //余额 /*set方法: public void set属性名(形参类型 形参名){ this.属性名=形参名 } 1. 形参类型和属性类型必须一致 2. 形参名与属性名一致 3. 方法中的属性名必须大写 * */ public void setNo(String no) { this.no = no; } /* public 返回值类型 get属性名(){ return 属性; } //1. 返回值类型与属性类型一致 //2. 方法中的属性名必须大写 * */ public String getNo() { return no; } // get方法 public String getPassword(){ return password; } //set方法 赋值前需要先验证数据的合法性 public void setPassword(String password){//设置密码 if(password.length()!=8){ System.out.println("温馨提示:设置密码必须为六位"); return;// 如果密码不等于8我们则退出,不复制 } this.password=password; } } class TestAccount { public static void main(String[] args) { //1. 创建对象 Account account = new Account(); //2.设置属性 account.setNo("123455"); account.setPassword("12345678"); //3.访问属性 System.out.println(account.getNo()); System.out.println(account.getPassword()); } }
封装的意义
1. 没有封装: 超市没有看守,无人管理,无法保护客户与商品的安全 2. 属性私有: 封住超市的所有入口,大家都不能进超市 3. 封装后: 设置了出口(get)与进口(set),在出入口增加了扫描仪器等设备,保障客户与商品的安全
设置只读属性:只提供get方法,不提供set方法
package com.tjcu.whj.account; /** * @author Wanghj * @version 1.0 * @date 2022/12/4 9:14 * 创建一个类,创建一个模板 */ public class Account { private String no; //卡号 private String password; //密码 private double balance=1000; //余额 /*set方法: public void set属性名(形参类型 形参名){ this.属性名=形参名 } 1. 形参类型和属性类型必须一致 2. 形参名与属性名一致 3. 方法中的属性名必须大写 * */ public void setNo(String no) { this.no = no; } /* public 返回值类型 get属性名(){ return 属性; } //1. 返回值类型与属性类型一致 //2. 方法中的属性名必须大写 * */ public String getNo() { return no; } // get方法 public String getPassword(){ return password; } //set方法 赋值前需要先验证数据的合法性 public void setPassword(String password){//设置密码 if(password.length()!=8){ System.out.println("温馨提示:设置密码必须为六位"); return;// 如果密码不等于8我们则退出,不复制 } this.password=password; } //只对余额提供get方法,不提供set public double getBalance(){ return balance; } } class TestAccount { public static void main(String[] args) { //1. 创建对象 Account account = new Account(); //2.设置属性 account.setNo("123455"); account.setPassword("12345678"); //3.访问属性 System.out.println(account.getNo()); System.out.println(account.getPassword()); System.out.println(account.getBalance()); } }
私有方法更加安全,仅供内部调用
public class Computer { public void on(){ startFun(); }//开机 private void startFun(){ System.out.println("供电,检测系统有误故障,主机是否烧坏"); } }
5、总结
意义: 竟尽可能的隐藏内部对象的实现细节,控制用户对对象的修改和访问权限,保障内部属性的安全
实现方式
1. 属性私有 private 2. 根据需要提供get/set方法,无特殊情况,两个方法都要提供。
二、继承
博客封装知识点配套视频:https://www.bilibili.com/video/BV1ve411N7Ay/?spm_id_from=333.999.0.0
1、is关系
- 概念
is a :爷爷==》爸爸==》我==》儿子==》孙子
什么是一种什么。例;
前者一定具备后者的特征与行为
2、计算机里面的is a关系
计算机的is a关系叫继承,是一种类与类之间的关系,前者称为父类,后者称为子类(派生类),当继承关系建立时,子类也能拥有父类中的特性(属性)与方法(方法)
- 语法
class 类名 extends 父类名{ }
举例: hp继承电脑的开机功能
public class Computer { public void on(){ startFun(); }//开机 private void startFun(){ System.out.println("供电,检测系统有误故障,主机是否烧坏"); } } class Hp extends Computer { } class testHp{ public static void main(String[] args) { Hp hp = new Hp(); hp.on(); } }
3、继承效果
子类可以继承父类的属性和方法
package com.tjcu.whj.account; /** * @author Wanghj * @version 1.0 * @date 2022/12/4 10:28 */ public class Computer { public void on() { startFun(); }//开机 private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } private void startFun() { System.out.println("供电,检测系统有误故障,主机是否烧坏"); } } class Hp extends Computer { } class testHp { public static void main(String[] args) { Hp hp = new Hp(); hp.on(); hp.setName("惠普"); System.out.println(hp.getName()); } }
继承的传递性:子类可以有父类,父类可以有父类,父类的父类可以还有父类,所以属性和方法会一直向下传递
package com.tjcu.whj; /** * @author Wanghj * @version 1.0 * @date 2022/12/4 11:07 */ public class TestIntan { public static void main(String[] args) { MyClassF myClassF = new MyClassF();//孙子 System.out.println(myClassF.); } } class MyClassA{//祖祖 int a; } class MyClassB extends MyClassA{//爷爷 int b; } class MyClassC extends MyClassB{//爸爸 int c; } class MyClassD extends MyClassC{//我 int d; } class MyClassE extends MyClassD{//儿子 int e; } class MyClassF extends MyClassE{//孙子 int f; }
java中的继承关系为单继承(符合中国原来的独生子女政策),一个类只能有一个直接父类,但是可以间接继承(多层继承)
3、访问修饰符(必须掌握)
作用组件作用范围
当什么都不写的时候,默认为default
4、方法的覆盖
使用于子类父类之间,子类可以继承父类的方法也可以覆盖掉父类的方法
public class Father { protected void eat(){ System.out.println("吃饭"); } } class My extends Father{ //覆盖:访问权限修饰符必须相同或更宽,返回值类型,方法名,参数表必须相同 public void eat(){ System.out.println("王恒杰喜欢吃烧烤"); } } class testMy{ public static void main(String[] args) { My my = new My(); my.eat(); } }
覆盖定义:访问权限修饰符必须相同或更宽,返回值类型,方法名,参数表必须相同
重载:方法名相同,返回值类型相同,参数列表不同(个数,顺序,类型)
三、复习
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VP14EaUy-1670127776630)(C:\Users\王恒杰\AppData\Roaming\Typora\typora-user-images\image-20221204121612674.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pcihfm5D-1670127776630)(C:\Users\王恒杰\AppData\Roaming\Typora\typora-user-images\image-20221204121626995.png)]
四、面向对象封装和继承课后习题(答案版本)
考察知识点:
类的定义
类的组成
对象的创建
客观题
1.(类与对象的概念)下列描述错误的是( c )
a. 类是对象的模板
b. 对象是类的实例
c. 面向对象思想是将功能拆分为若干小步,逐步求精解决问题
d. 面向对象是从对象入手,把涉及到的所有对象找到,借助于对象和对象间相互配合,实现需求
2.(实例变量) 下列关于属性描述不正确的是( c )
a. 属性定义在类以内,方法以外
b. 属性又称为成员变量
c. 属性的作用范围从定义开始,到定义它的代码块结束
d. 属性定义后可以在本类中直接使用
3.(方法重载)有关方法重载描述错误的是( d )
a. 方法名相同
b. 返回值类型相同
c. 参数列表不同
d. 参数列表相同
4.(方法重载)下列和 public void method(){}不能构成方法重载的是( d )
a. public void method(double d){}
b. public void method(int n){}
c. public int method(int n){}
d. public int method(){}
5.(构造方法)下列对构造方法描述不正确的是( c )
a. 构造方法是特殊的方法,允许重载
b. 方法名必须和类名相同
c. 一个类必须手动提供构造方法
d. 构造方法不允许手动调用
6.(重载,构造方法)已知有以下代码:
public class ClassA{ public void ClassA(String str){} public void test(){} }
对以上程序描述正确的是( c )
a. 编译报错
b. ClassA类中定义了有参数的构造方法
c. ClassA类中只有一个默认的无参数的构造方法
d. test方法不允许手动的调用
7.(创建对象) 已知有以下代码:
public class Test{ public static void main(String[] args){ Worker w = new Worker(); w.work(); } } class Worker{ String name; public Worker(String n ){ name = n; } public void work(){ System.out.println("working....."); } }
对以上程序描述正确是的( a )
a. 没有无参构造方法,编译报错
b. 没有为name属性赋值,编译报错
c. 输出 working…
d. 编译成功,运行报错
8.(构造方法)构造方法什么时候会被使用( b )
a. 类定义时
b. 创建对象时
c. 调用对象方法时
d. 使用对象引用时
9.(重载,实例变量)根据以下代码,请选择正确结果( c )
class ClassA{ public void method(int value){ System.out.println( value ); } public void method(){ System.out.println( value ); } int value; } public class TestClassA{ public static void main(String[] args){ ClassA ca = new ClassA(); ca.value = 10; ca.method(); ca.method(20); } }
a. 编译不通过
b. 输出10 10
c. 输出10 20
d. 输出0 20
10.(方法重载)有以下代码
class ClassA{ public void method(){ System.out.println("method()"); } public void method(int i){ System.out.println("method(int)"); } public static void main(String[]args){ ClassA ca = new ClassA(); ca.method(); ca.method( 10 ); } }
该程序是否能编译通过?
如果可以,写出该程序运行结果,如果不能,请说明理由,以及如何修改。
可以 运行结果为:
method() method(int)
11.(构造方法)关于构造方法,下列说法正确的是( b d )[多选]
a. 每个类中都有至少需要写一个构造方法
b. 一个类中可以有多个构造方法
c. 构造方法可以有返回值
d. 构造方法可以有多个参数
12.(构造方法)有以下代码:
class ClassA{ int value; } public class TestMyClass{ public static void main(String[]args){ MyClass mc1 = new MyClass(); MyClass mc2 = new MyClass(10); System.out.println( mc1.value ); System.out.println( mc2.value ); } }
这个程序能否编译通过?如果可以,输出结果是什么;如果不可以,应该如何修改?
不正确,应改为
class MyClass{ int value; public MyClass(){} public MyClass(int a){ value = a; } } public class TestMyClass{ public static void main(String[]args){ MyClass mc1 = new MyClass(); MyClass mc2 = new MyClass(10); System.out.println( mc1.value ); System.out.println( mc2.value ); } }
主观题
1.(面向对象基础)根据注释,把//1//2//3//4//5//6处代码补充完整
class Dog{ //定义一个name属性,该属性为String类型 String name; //定义一个sex属性,该属性为boolean类型 boolean sex; public Dog(){} public Dog(String n,boolean s){ //分别根据参数,设置Dog类的属性 name = n; sex = s; } public void play(){ System.out.println(name+"play"); } public void play(int n ){ System.out.println(name+"play"+n+"minutes"); } } public class TestDog{ public static void main(String[]args){ Dog d; //使用有参构造方法创建一个Dog对象,名字为joy,性别为公(false) d = new Dog("joy",false); //调用Dog对象的无参play方法 d.play(); //调用Dog对象的有参play方法,参数为30 d.play(30); } }
2.编程:(面向对象基础–工人类)写一个Worker类,并创建多个Worker对象
①为Worker类添加三个属性:
String类型的name,表示工人的姓名
int类型的age,表示工人的年龄
double类型的salary,表示工人的工资
②为Worker类添加两个构造方法:
公开无参构造方法
接受三个参数的构造方法,三个参数分别为String、int和double
③为Worker类添加两个work(工作) 方法:
无参的work方法,打印工人在工作
带有整数参数的方法,表示工人工作的时间(单位:小时),打印工人工作了几小时
代码示例:
class Worker{ String name;//姓名 int age;//年龄 double salary;//工资 //构造方法 public Worker(){} public Worker(String n,int a, double s){ name = n; age = a; salary = s; } //普通方法 public void work(){ System.out.println("工人在工作"); } public void work(int hours){ System.out.println("工人工作了"+hours+"小时"); } }
3.编程:(面向对象基础–地址类)创建一个Address类,定义两个属性,String类型的address表示地址,String类型的zipCode表示邮编,并提供有参、无参构造方法
代码示例:
class Address{ String address;//地址 String zipCode;//邮编 //构造方法 public Address(){} public Address(String a,String z){ address = a; zipCode = z; } }
4.编程:定义一个Teacher类,姓名(String) 年龄(int) 性别(boolean),要求如下:
① 提供无参数、有参数的构造方法
② 功能方法teach(),返回值类型为void,打印姓名+“正在上课”
③ 编写测试类,创建年龄为16岁的小小老师,调用teach方法
代码示例:
//教师类 class Teacher{ String name;//姓名 int age;//年龄 boolean sex;//性别(false=男、true=女) //构造方法 public Teacher(){} public Teacher(String n,int a,boolean s){ name = n; age = a; sex = s; } public void teach(){ System.out.println(name+"正在上课"); } } //测试类 public class TestTeacher{ public static void main(String[]args){ //创建对象 Teacher t = new Teacher("小小",18,true); //调用方法 t.teach(); } }
5.编程:定义一个网络用户类(User 类),需要处理的信息有用户 id、用户密码 password、邮箱地址(email),要求如下:
① 提供带有两个参数的构造方法,为用户 id 和用户密码赋值,此时 email 采用默认的:id加上"@zparkhr.com.cn"赋值;
②提供带有三个参数的构造方法
③提供一个show 方法,用于展示用户的信息,密码处理为 *** 形式显示
④定义一个测试类,创建对象,并展示用户的信息
代码示例:
//用户类 class User{ String id;//用户id String password;//密码 String email;//邮箱 //构造方法 public User(String i,String p){ id = i; password = p; //邮箱默认为id+@zparkhr.com.cn email = id+"@zparkhr.com.cn"; } public User(String i,String p,String e){ id = i; password = p; email = e; } //普通方法 public void show(){ System.out.println("用户id:"+id); System.out.println("密码:******"); System.out.println("邮箱:"+email); } } //测试类 public class TestUser{ public static void main(String[]args){ //创建对象 User u = new User("zhangsan","123456"); //调用show方法 u.show(); } }
6.编程:定义一个 Book 类(代表教材),具有属性名称(title)、页数(pageNum),要求如下:
① 提供无参数和有参数的构造方法
② 编写一个测试类,创建对象并为属性赋值,将对象的信息展示在控制台上
代码示例:
//教材类 class Book{ String title;//名称 int pageNum;//页数 //构造方法 public Book(){} public Book(String t,int n){ title = t; pageNum = n; } } //测试类 public class TestBook{ public static void main(String[]args){ //创建对象 Book b = new Book("CoreJava从入门到跑路",5000); //打印各个属性 System.out.println( b.title ); System.out.println( b.pageNum ); } }
7.编程:定义一个 Student 类,具有属性有名字(name)、年龄(age)、地址(address)、 邮编(zipcode)、电话(mobile)
① 为 Student 类添加一个 getPostAddress 方法,要求返回 Student 对象的地址和邮编
②定义一个测试类,创建对象并为属性赋值,将用户的信息进行展示
代码示例:
class Student{ String name; int age; String address; String zipCode; String mobile; public Student(String n,int a,String ad,String z, String m){ name = n; age = a; address = ad; zipCode = z; mobile = m; } //返回地址与邮编,地址与邮编都是String类型,采用字符串拼接并返回 public String getPostAddress(){ return address + zipCode; } }
8.编程:模拟简单的计算器,定义一个类(Number),类中定义两个整数类型的属性值 double v1和double v2;
①提供两个构造方法,可以为属性赋值
②提供加( add() )、减( subtration() )、乘( multip() )、除( division() )功能方法
③定义一个测试类,创建该类对象,并通过调用方法完成两个数值的加、减、乘、除运算
代码示例:
class Number{ double v1; double v2; //构造方法 public Number(){} public Number(double a,double b){ v1 = a; v2 = b; } //加方法 public double add(){ return a+b; } //减方法 public double subtration(){ return a-b; } //乘方法 public double multip(){ return a*b; } //除方法 public double division(){ return a/b; } } //测试类 public class TestNumber{ public static void main(String[]args){ //创建对象 Number n = new Number(10,2); //调用方法获取结果 double r1 = n.add(); double r2 = n.subtration(); double r3 = n.multip(); double r4 = n.division(); //打印输出结果 System.out.println( r1 ); System.out.println( r2 ); System.out.println( r3 ); System.out.println( r4 ); } }
提高题
1.编程:在编程题3、4题的基础上为工人类中添加属性地址addr,该属性的类型为Address类型
要求:
①创建工人对象,其姓名为“zhangsan”,年龄为25,工资为2500
②创建地址对象,家庭住址为“北京市海淀区清华园1号”,邮政编码为100084。
③使用地址对象为工人对象中的addr属性赋值,表示工人的地址
代码示例:
//1.创建Worker对象 Worker worker = new Worker("zhangsan", 25, 2500D); //2.创建地址对象 Address address = new Address("北京市海淀区", "100094"); //3.为worker对象中的addr属性赋值 //方式一:创建好对象后再为属性赋值 worker.addr = address; //方式二:创建对象时使用构造方法为属性赋值 Worker worker2 = new Worker("lisi",28,5000D,address); //打印输出worker中的信息与地址信息 System.out.println(worker.name); System.out.println(worker.age); System.out.println(worker.salary); //链式访问,获取worker对象中的addr属性,再访问addr对象中的address属性 System.out.println(worker.addr.address); System.out.println(worker.addr.zipCode); double r4 = n.division(); //打印输出结果 System.out.println( r1 ); System.out.println( r2 ); System.out.println( r3 ); System.out.println( r4 ); } }
提高题
1.编程:在编程题3、4题的基础上为工人类中添加属性地址addr,该属性的类型为Address类型
要求:
①创建工人对象,其姓名为“zhangsan”,年龄为25,工资为2500
②创建地址对象,家庭住址为“北京市海淀区清华园1号”,邮政编码为100084。
③使用地址对象为工人对象中的addr属性赋值,表示工人的地址
代码示例:
//1.创建Worker对象 Worker worker = new Worker("zhangsan", 25, 2500D); //2.创建地址对象 Address address = new Address("北京市海淀区", "100094"); //3.为worker对象中的addr属性赋值 //方式一:创建好对象后再为属性赋值 worker.addr = address; //方式二:创建对象时使用构造方法为属性赋值 Worker worker2 = new Worker("lisi",28,5000D,address); //打印输出worker中的信息与地址信息 System.out.println(worker.name); System.out.println(worker.age); System.out.println(worker.salary); //链式访问,获取worker对象中的addr属性,再访问addr对象中的address属性 System.out.println(worker.addr.address); System.out.println(worker.addr.zipCode);