【Java复健指南06】OOP中级01-封装、继承、super
注:从OOP中级部分开始使用IDEA构建代码
封装
封装的实现步骤
1)将属性进行私有化private【不能直接修改属性】
2)提供一个公共的set方法,用于对属性判断并赋值
public void setXxx(类型参数名){
//加入数据验证的业务逻辑
//属性=参数名;
}
3)提供一个公共的get方法,用于获取属性的值
public XX getXxx(){//权限判断
return xx;
}
示例
public class Encapsulation01 {
/*
在java中如何实现这种类似的控制呢?
请编写一个小程序,不能随便查看人的年龄,工资等隐私,
并对设置的年龄进行合理的验证。年龄合理就设置,
否则给默认年龄,必须在1-120,
年龄,工资不能直接查看﹐ name的长度在2-6字符之间
*/
public static void main(String[] args) {
Person person = new Person();
person.setName("jack");
person.setAge(18);
person.setSal(8000);
System.out.println(person.info());
}
}
class Person{
public String name;
private int age;
private double sal;
//自己写set、get太慢,快捷键Alt+Ins,getter
public String getName() {
return name;
}
public void setName(String name) {
//加入对数据的校验
if(name.length() >= 2 && name.length() <= 6){
this.name = name;
}else {
System.out.println("需要一个2-6字符间的名字,当前默认为sb");
this.name = "sb";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
//判断
if(age >= 1 && age <= 120){
this.age = age;
}else {
System.out.println("年龄需要在1-120之间,当前为默认年龄24");
this.age = 24;//否则给个默认年龄
}
}
public double getSal() {
//可以增加对薪水查看权限的判断
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
//写一个方法,返回属性信息
public String info(){
return "信息为 name=" + name+ " age=" + age +" 薪水=" + sal;
}
}
封装练习
创建程序,在其中定义两个类:Account和AccountTest类。
1. Account类要求具有属性:姓名(长度为2位3位或4位)、余额(必须>29)
密码(必须是六位),如果不满足,则给出提示信息,并给默认值
2. 通过setXxox的方法给Account的属性赋值。
3. 在AccountTest中测试
Account.java
package com.Tsengstu.encap;
public class Account {
//为了封装设置私有属性
private String name;
private double balance;
private String pwd;
//提供两个构造器
//有显示声明构造器就必须写一个空构造器,否则不会自动调默认构造器,进而导致报错
public Account() {
}
public Account(String name, double balance, String pwd) {
this.setName(name);
this.setBalance(balance);
this.setPwd(pwd);
}
public String getName() {
return name;
}
//设置姓名要求
public void setName(String name) {
if(name.length() >= 2 && name.length() <= 4){
this.name = name;
}else {
System.out.println("姓名(长度为2位3位或4位),当前默认值为ad");
this.name = "ad";
}
}
public double getBalance() {
return balance;
}
//设置余额
public void setBalance(double balance) {
if(balance >= 20){
this.balance = balance;
}else {
System.out.println("余额(必须>29),默认为0");
this.balance = 0;
}
}
public String getPwd() {
return pwd;
}
//设置密码
public void setPwd(String pwd) {
if(pwd.length() == 6){
this.pwd = pwd;
}else {
System.out.println("密码(必须是六位),默认值是000000");
this.pwd = "000000";
}
}
//显示账号信息
public void showInfo() {
//可以增加权限的校验
System.out.println("账号信息 name=" + name + " 余额=" + balance + " 密码:" + pwd);
// if(){
// System.out.println("账号信息 name=" + name +"余额=" + balance +"密码"+pwd);
// }else{
// System.out.println("你无权查看..."");
// }
}
}
AccountTest.java
package com.Tsengstu.encap;
public class AccountTest {
public static void main(String[] args) {
Account account = new Account();
account.setName("jk");
account.setBalance(66);
account.setPwd("123456");
account.showInfo();
}
}
继承
若两个类中的很多属性和方法相同,可以使用继承来提高代码的复用性。
当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
继承的基本语法
class 子类 extends 父类{
}
1)子类就会自动拥有父类定义的属性和方法;
2)父类又叫超类,基类;
3)子类又叫派生类;
例子
现在有这些类,关系如下
Student是pupil和graduate的父类
package com.Tsengstu.extend_.improve_;
//父类,是pupil和graduate的
public class Student {
//共有属性
public String name;
public int age;
private double score;
//共有方法
public void setScore(double score) {
this.score = score;
}
public void showInfo() {
System.out.println("学生名 " + name + " 年龄" + age + " 成绩" + score);
}
}
那么pupil和graduate可以通过extends关键字去继承父类的公共属性和方法,例如pupil
package com.Tsengstu.extend_.improve_;
public class Pupil extends Student{
public void testing(){
System.out.println("小学生"+name+"正在考小学语文");
}
}
在测试类中可以调用如下
package com.Tsengstu.extend_;
public class Extends01 {
public static void main(String[] args) {
Pupil pupil = new Pupil();
pupil.name ="小明";
pupil.age = 10;
pupil.testing();
pupil.setScore(60);
pupil.showInfo();
}
}
继承的细节讨论
1.
子类继承了所有的属性和方法,但是私有属性和方法不能在子类直接访问(非私有可直接访问),要通过公共的方法去访问
例子
创建三个类:ExtendsDetail(用于测试)、Base(父类)、Sub(子类)
父类中定义如下
package com.Tsengstu.extend_;
public class Base {//父类
//4个属性
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
//父类提供一个公共方法返回私有属性(n4)
public int getN4(){
return n4;
}
//父类提供一个公共方法返回私有方法(test400())
public void callTest400(){
test400();
}
public Base() {//无参构造器
System.out.println("Base()....");
}
public void test100() {
System.out.println("test100");
}
protected void test200() {
System.out.println("test200");
}
void test300() {
System.out.println("test300");
}
private void test400() {
System.out.println("test400");
}
}
子类定义如下,其中私有属性/方法无法直接访问,需要通过父类中的公共方法间接获取
public class Sub extends Base {
public Sub() {//构造器
System.out.println("sub()....");
}
public void say0k() {//子类方法
//非私有的属性和方法可以在子类直接访间
System.out.println(n1 + " "+n2 + " "+n3 + " ");//私有的n4用不了
test100();
test200();
test300();//私有的test400()用不了
//可通过父类提供的公共方法去访问私有属性和方法
System.out.println("n4= " + getN4());
callTest400();
}
}
测试
public class ExtendsDetail {
public static void main(String[] args) {
Sub sub = new Sub();
sub.say0k();
sub.callTest400();
}
}
2.
子类必须调用父类的构造器,完成父类的初始化
其实是因为在子类构造器中会默认存在"super();"(写不写都有)
其会去初始化父类的构造器,利用这点可以加载一些父类的参数
3.
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,
如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过[举例说明]
4.
如果希望制定调用父类的某个构造器,则显式的调用一下
5.
super在使用时需要放在构造器的第一行
6.
super()和this()都需要放在构造器的第一行,且不能同时出现
7.
java所有类都是Object类的子类
8.
父类的调用不仅限于父类,会一直向上追溯到Object类(顶级父类)
子类最多只能继承一个父类(指直接继承),即java中是单继承机制
思考,如何让A类继承B类和C类?先让A继承B,再让B继承C
继承练习题
一、"this()"与"super();"
public class ExtendsExercise01 {
public static void main(String[] args) {
/*
* 解析
* 创建对象b,会先调用B类的无参构造器B()
* 在无参构造器中有个this("abc")【有this无super】
* 相当于去调用本类中带参数的构造器B(String name)
* 然后该构造器中会有一个默认的"super();"去初始化其继承的父类的构造器
* 于是调用了A()
* 那么输出结果应该是
* a
b name
b
* */
B b = new B();
}
}
class A{
A(){
System.out.println("a");
}
A(String name){
System.out.println("a name");
}
}
class B extends A{
B(){
this("abc");
System.out.println("b");
}
B(String name){
System.out.println("b name");
}
}
主要体现了继承中向上索引和子类默认有super();的机制
二、
编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
编写PC子类,继承Computer类,添加特有属性【品牌brand】
编写NotePad子类,继承Computer类,添加特有属性【演示color】
编写Test类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法并打印输出信息。
public class ExtendsExercise02 {
public static void main(String[] args) {
//非要写个测试类也行,保证在同一个包下就行,这里直接在一个类里写了
Computer computer = new Computer("i9-13110K","64G 5899MHz", "8T");
System.out.println(computer.getDetails());
PC pc = new PC("i9-13110K","64G 5899MHz", "8T", "ROG",25999);
System.out.println(pc.getDetails());
NotePad notePad = new NotePad("M1 Max","12G", "512GB","black", 8.5);
System.out.println(notePad.getDetails());
}
}
class Computer{
String CPU;
String RAM;
String HDD;
public Computer() {
}
public Computer(String CPU, String RAM, String HDD) {
this.CPU = CPU;
this.RAM = RAM;
this.HDD = HDD;
}
public String getDetails(){
return "电脑CPU: "+CPU+" 运行内存: "+RAM+" 硬盘容量: "+HDD;
}
}
class PC extends Computer{
String brand;
int price;
public PC(){
}
public PC(String brand, int price){
this.brand = brand;
this.price = price;
}
public PC(String CPU, String RAM, String HDD, String brand, int price){
super(CPU, RAM, HDD);//用this也行
this.brand = brand;
this.price = price;
}
public String getDetails(){
return "笔记本电脑CPU: "+CPU+" 运行内存: "+RAM+" 硬盘容量: "+HDD+" 笔记本电脑品牌: "+brand+" 笔记本电脑价格: "+price;
}
}
class NotePad extends Computer{
String color;
double screen_inch;
public NotePad(){
}
public NotePad(String color, double screen_inch) {
this.color = color;
this.screen_inch = screen_inch;
}
public NotePad(String CPU, String RAM, String HDD, String color, double screen_inch){
super(CPU, RAM, HDD);
this.color = color;
this.screen_inch = screen_inch;
}
public String getDetails(){
return "平板电脑CPU: "+CPU+" 运行内存: "+RAM+" 硬盘容量: "+HDD+" 平板电脑颜色: "+color+" 平板电脑尺寸: "+screen_inch;
}
}
Super
基本介绍
super代表父类的引用,用于访问父类的属性、方法、构造器基本语法
1.访问父类的属性,但不能访问父类的private属性
super.属性名;
- 访问父类的方法,不能访问父类的private方法
super.方法名(参数列表); - 访问父类的构造器(这点前面用过):
super(参数列表);只能放在构造器的第一句,只能出现一句
super给编程带来的便利/细节
-
调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子
类初始化) -
当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须
通过super。如果没有重名,使用super、this、直接访问是一样的效果! -
super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用
super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。
A->B->C