封装
定义
封装就是在对象的外部,为对象的属性赋值,可能存在非法数据的录入。所以我们要控制属性的赋值。
String name="da";
int age=3000;
char sex='男';
double score=15.6;
使用private封装age属性,并且添加getAge、setAge方法用于获取、设置age的值,然后在setAge方法中对age的值进行控制。
访问修饰符
本类 | 同包 | 非同包子类 | 其它 | |
---|---|---|---|---|
private | √ | × | × | × |
default | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
按照权限从⼤到⼩排列为 public > protected > default > private
案例:
需求:
实现银行功能
用户输入正确卡号和密码后可执行以下操作
菜单如下:1.存款2.取款3.转账4.查询余额5.修改密码0.退出
分析:
User类(cardNo、identity、username、password、phone、balance)
Bank类主要包括以下功能
初始化用户(initial)
用户登录(login)
显示菜单(showMenu)
存款(save)、取款(withDraw)、转账(trans)、查询余额(query)、修改密码(modifyPassword)
package work;
import java.util.Scanner;
/**
* @Author night
* @Date 2021/8/12 15:43
*/
class TestCard {
public static void main(String[] args) {
Card card = new Card();
card.initial();
}
}
public class Card {
/**
* 卡号、身份证号码、姓名、密码、联系方式、余额
*/
private String cardNo;
private String identity;
private String username;
private String password;
private String phono;
private double balance;
//因为经常使用,直接创建实例变量,在全局中都可以使用
Scanner scanner = new Scanner(System.in);
public void initial() {
System.out.println("开始注册用户信息:");
System.out.print("请输入姓名:");
username = scanner.next();
System.out.print("请输入密码:");
password = scanner.next();
System.out.print("请输入身份证号码:");
identity = scanner.next();
System.out.print("请输入联系方式:");
phono = scanner.next();
System.out.print("请输入银行卡号:");
cardNo = scanner.next();
System.out.print("请输入余额:");
balance = scanner.nextDouble();
System.out.println("********用户信息如下********");
System.out.println("姓名:" + username + "\t\t" + "密码:" + password + "\t\t" + "身份证号码:" + identity);
System.out.println("联系方式:" + password + "\t\t" + "卡号:" + cardNo + "\t\t" + "余额:" + balance);
login();
}
/**
* 登录方法
*/
public void login() {
String name;
String pwd;
System.out.println("********请登录********");
System.out.print("请输入姓名:");
name = scanner.next();
System.out.print("请输入密码:");
pwd = scanner.next();
//校验密码是否正确
if (name.equals(username) && pwd.equals(password)) {
System.out.println("********登录成功,欢迎你********");
showMenu();
} else {
System.out.println("********登录失败,请重新登录********");
login();
}
}
//显示菜单方法
public void showMenu() {
System.out.println("1、存款 2、取款 3、转账 4、余额查询 5、修改密码 0、退出");
int choose;
System.out.print("请选择业务:");
choose = scanner.nextInt();
switch (choose) {
case 1:
System.out.println("********进入存款********");
save();
break;
case 2:
System.out.println("********进入取款********");
withDraw();
break;
case 3:
System.out.println("********进入转账********");
trans();
break;
case 4:
System.out.println("********查询余额********");
query();
break;
case 5:
System.out.println("********修改密码********");
modifyPassword();
break;
case 0:
return;
}
}
//存款方法
public void save() {
System.out.print("请输入存进的金额:");
int money = scanner.nextInt();
balance = balance + money;
System.out.print("您的余额:" + balance);
System.out.println();
showMenu();
}
//取款方法
public void withDraw() {
System.out.print("请输入取出的金额:");
int money = scanner.nextInt();
//取款数大于余额无法取款
if (money > balance) {
System.out.println("余额不足,请重新输入");
withDraw();
}
balance = balance - money;
System.out.print("您的余额:" + balance);
System.out.println();
showMenu();
}
//转账
public void trans() {
System.out.print("请输入转入的卡号:");
String orderCardNo = scanner.next();
//自己不能向自己的卡内转账
if (orderCardNo.equals(cardNo)) {
System.out.println("自己不能向自己的卡内转账");
trans();
}
System.out.print("请输入转入的金额:");
double money = scanner.nextDouble();
//转账数大于余额无法转账
if (money > balance) {
System.out.println("余额不足,请重新输入");
trans();
}
balance = balance - money;
System.out.print("您已向卡号为:" + orderCardNo + "转入" + money + "您卡内余额为" + balance);
System.out.println();
showMenu();
}
public void query() {
System.out.print("您的余额:" + balance);
System.out.println();
showMenu();
}
String pwd1 = null;
public void modifyPassword() {
System.out.println("请输入密码:");
pwd1 = scanner.next();
System.out.println("请确认密码:");
String pwd2 = scanner.next();
//校验两次输入的密码是否相同
if (pwd1.equals(pwd2)) {
password = pwd1;
} else {
System.out.println("两次密码输入不一致,请重新输入:");
modifyPassword();
}
System.out.print("该账号的密码:" + password);
System.out.println();
showMenu();
}
//无参构造方法
public Card() {
}
//get and set
public String getCardNo() {
return cardNo;
}
public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}
public String getIdentity() {
return identity;
}
public void setIdentity(String identity) {
this.identity = identity;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhono() {
return phono;
}
public void setPhono(String phono) {
this.phono = phono;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
继承
定义
子类继承父类后,子类可以使用父类中的属性和方法,也可定义子类独有的属性和方法。
优缺点
优点
- 获取到⽗类中的⽅法和属性
- 提⾼了代码的复⽤性和维护性
- 类和类之间产⽣关系,是多态的前提
缺点
- 类的耦合性增强了 , 我们开发的原则是⾼内聚,低耦合
- 耦合: 类与类的关系
- 内聚: 独⽴完成⼯作的能⼒
格式
- class 子类 extends 父类{ } //定义子类时,显示继承父类
public class inherit {
public static void main(String[] args) {
//实例化子类
Dog dog = new Dog();
//子类通过继承调用父类的属性,从而给自己赋值
dog.name = "狗";
dog.age = 2;
dog.sex = "公";
dog.hairColor="白色";
System.out.println(dog.name+dog.hairColor+dog.sex+dog.age);
//调用子类特有的方法
dog.run();
//子类调用父类的方法
dog.eat();
dog.sleep();
}
}
//父类
class Animal {
public String name;
public int age;
public String sex;
public void eat() {
System.out.println("吃.....");
}
public void sleep() {
System.out.println("睡.....");
}
}
//子类使用extends继承父类
class Dog extends Animal {
public String hairColor;
//子类特有的方法
public void run() {
System.out.println("跑.....");
}
}
不可继承
构造方法:
类中的构造方法,只负责创建本类对象,不可继承。
private修饰的属性和方法:(权限不足)
访问修饰符的一种,仅本类可见。
父子类不在同一个package中时,default修饰的属性和方法:(权限不足)
访问修饰符的一种,仅同包可见。
注意事项
-
this和super关键字
-
this代表本类对象,super代表父类对象
-
this.成员 调用本类成员,也可以调用父类中的成员(当⼦类中没有的时候)
super.成员 调用父类的成员
-
this(…) 调⽤本对象的构造⽅法
super(…) 调⽤⽗对象的构造⽅法
public class Father { String name = "⼩红"; public void method(){ System.out.println(name); } } public class Son extends Father{ String name = "⼩张"; public void show(){ System.out.println(this.name); //调⽤⽗类的成员变量 System.out.println(super.name); } } public class TestDemo { public static void main(String[] args) { Son son = new Son(); son.method(); } }
-
-
继承中构造⽅法的关系
- ⼦类中的所有构造⽅法都会默认访问⽗类中的⽆参构造⽅法
- 因为⼦类继承⽗类中的数据 ,可能还会使⽤⽗类中的数据, 所有⼦类在初始化的时候, 需要将⽗类先初始化
- 每⼀个⼦类构造⽅法的第⼀条语句默认都是:super(), Object类最顶层的⽗类
public class Son extends Father{ String name = "⼩张"; public Son(String name) { //会默认调⽤⽗类⽆参的构造⽅法 super(); this.name = name; } }
- 当⽗类中没有空参构造的时候, ⼦类会报错, 这时我们可以采⽤调⽤⽗类的有参构造,并赋上默
认值
-
继承中成员的关系
- ⼦类⽆法使⽤⽗类私有的成员
public class Father { private String name = "⼩明"; private void show(){ System.out.println(name); } } public class Son extends Father{ public void method() { //报错,找不到 System.out.println(name); } } public class TestDemo { public static void main(String[] args) { Son son = new Son(); //报错, 找不到show⽅法 son.show(); son.method(); } }
- ⼦⽗类中出现了⼀样的成员变量, 还是采⽤就近原则
public class Father { String name = "⼩红"; public void show(){ System.out.println(name); } } public class Son extends Father{ String name = "⼩明"; public void method() { String name = "⼩刚"; System.out.println(name); System.out.println(this.name); System.out.println(super.name); } } public class TestDemo { public static void main(String[] args) { Son son = new Son(); son.show(); son.method(); } }
-
继承中的初始化顺序
- 加载⽗类 加载⼦类 ⽗类类初始化 ⼦类类初始化 创建⽗类对象 创建⼦类对象 初始化⽗类 初始
化⼦类 - 静态优先 ⽗类优先 成员变量----构造代码块 ----- 构造⽅法
- 加载⽗类 加载⼦类 ⽗类类初始化 ⼦类类初始化 创建⽗类对象 创建⼦类对象 初始化⽗类 初始
-
注意事项
- 使⽤时, 尽量使⽤最底层的⼦类, 最底层的⼦类的功能是最全的
- 查看这个体系中共性的功能, 就去查看顶层的⽗类
- 不要为了继承⽽继承
重写
定义
- ⼦⽗类中出现了⼀模⼀样的⽅法
- 重写是⼀种现象, 也是⼀个动作
- 当⼦类需要⽗类的功能, ⽽功能主体⼦类有⾃⼰特有内容时, 可以重写⽗类的⽅法, 这样 , 既延
续了⽗类的功能 , ⼜定义了⼦类特有的内容
原则
方法名称、参数列表、返回值类型必须与父类相同。
访问修饰符不能比父类更严格。
执行
子类覆盖父类方法后,调用时优先执行子类覆盖后的方法。
public class inherit {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
}
}
//父类
class Animal {
public String name;
public int age;
public String sex;
public void eat() {
System.out.println("吃.....");
}
public void sleep() {
System.out.println("睡.....");
}
}
//子类
class Dog extends Animal {
//重写父类eat方法
@Override
public void eat() {
System.out.println("吃狗粮");
}
//子类特有方法
public void run() {
System.out.println("跑.....");
}
}
注意事项
- 私有方法
- ⽗类中私有⽅法不能被重写
- ⽗类私有⽅法⼦类根本⽆法继承
- 权限
- ⼦类重写⽗类⽅法时, 访问权限不能更低,最好保持⼀致
- 静态方法
- ⽗类的静态⽅法, ⼦类也必须通过静态⽅法重写
- 重写其实是全遮挡, 调⽤者⽆法在通过任何⽅式去调⽤和⼦类关联的⽗类对象中的⽅法
- 静态⽆法做到全遮挡, 我们依然可以直接使⽤⽗类中的⽅法
public class Father {
static String name = "⼩明";
public static void method(){
System.out.println("⽗类⽅法"+name);
}
}
public class Son extends Father{
public static void method() {
System.out.println("⼦类⽅法"+name);
}
}
public class TestDemo {
public static void main(String[] args) {
Son son = new Son();
son.name = "⼩明";
son.method();
Father.method();
}
}
instanceof
定义
判断⼀个对象是否属于⼀个类或者此类的⽗类
用法
- 对象 instanceof 类名
- 该表达式是⼀个⽐较运算符,返回的结果是boolean类型 true | false
public class TestDemo {
public static void main(String[] args) {
Son son = new Son();
//判断son的是否属于Son类型
boolean b = son instanceof Son;
System.out.println(b);
//判断son的类型是否属于Son类型的⽗类
b = son instanceof Father;
System.out.println(b);
}
}
super
概念
在子类中,可直接访问从父类继承到的属性和方法,但如果父子类的属性或方法存在重名(属性遮蔽、方法覆盖)时,需要加以区分,才可专项访问。
当子类继承父类,子类想要使用父类方法里面的方法体,可以使用super关键字完成
示例
Father类的show方法输出Father,Son类的show方法输出Son,如果实例化Son调用show方法并且想输出Father,就需要使用super.show();,这里的show方法调用的就是Father类的show方法
public class superInherit {
public static void main(String[] args) {
Son son = new Son();
son.show();
}
}
//父类
class Father {
public void show() {
System.out.println("Father");
}
}
//子类
class Son extends Father {
public void show() {
//调用父类的show方法
super.show();
System.out.println("Son");
}
}
不仅可以调用方法,还可以调用属性。
public class superInherit {
public static void main(String[] args) {
Son son = new Son();
son.print();
}
}
//父类
class Father {
int age = 10;
}
//子类
class Son extends Father {
int age = 20;
public void print() {
int age = 30;
System.out.println("局部变量:" + age);
System.out.println("实例变量:" + this.age);
System.out.println("父类变量:" + super.age);
}
}
super还可以调用父类的无参构造方法
public class superInherit {
public static void main(String[] args) {
Son son = new Son();
son.print();
}
}
//父类
class Father {
public Father() {
System.out.println("父类默认构造方法");
}
}
//子类
class Son extends Father {
public Son() {
//调用父类默认构造方法
super();
System.out.println("子类默认构造方法");
}
public void print() {
System.out.println("子类特有方法");
}
}
在子类中使用super();可以调用父类默认的构造方法,调用时必须放在第一行,和this一样。
程序执行顺序是:父类构造方法、子类构造方法、子类特有方法
子类还可以调用父类带参的构造方法
public class superInherit {
public static void main(String[] args) {
//实例化子类,并且调用的是带参构造方法,不是默认的无参构造方法
Son son = new Son(100, 200, 300);
son.print();
}
}
//父类
class Father {
int num1;
int num2;
public Father() {
System.out.println("父类默认构造方法");
}
//父类重载自己的构造方法:两个参数
public Father(int num1, int num2) {
this.num1 = num1;
this.num2 = num2;
System.out.println(num1 + "\t" + num2);
}
}
//子类
class Son extends Father {
int num3;
//子类重载自己的构造方法
public Son(int num1, int num2, int num3) {
//调用父类的带参构造方法
super(num1, num2);
this.num3 = num3;
System.out.println(num1 + "\t" + num2 + "\t" + num3);
}
public Son() {
//调用父类默认构造方法
super();
System.out.println("子类默认构造方法");
}
public void print() {
System.out.println("子类特有方法");
}
}
this和super的区别
- this表示当前对象引用,调用本类(包括继承)的属性、方法、本类构造方法
- super表示父类对象引用,调用父类的属性、方法、构造方法
- this和super不能同时使用,因为两个都必须在首行
public class superInherit {
public static void main(String[] args) {
//实例化子类,并且调用的是带参构造方法,不是默认的无参构造方法
new Son(10);
}
}
//父类
class Father {
public Father() {
System.out.println("父类默认构造方法");
}
}
//子类
class Son extends Father {
//子类重载自己的构造方法
public Son(int num1) {
this();
System.out.println(num1);
}
public Son() {
//调用父类默认构造方法
super();
System.out.println("子类默认构造方法");
}
}
执行流程:当实例化子类特有方法时,首先调用子类特有的方法,子类特有方法第一行是this();所以会去调用子类的无参构造方法,子类的无参构造方法第一行是super();所以调用父类的无参构造方法,父类的无参构造方法输出:父类默认构造方法,执行结束后掉头到子类无参构造方法执行输出:子类默认构造方法,执行结束后再掉头到子类带参构造方法,因为主方法在实例化是传入参数10,所以现在带参构造方法的num1=10,程序执行输出10。
5. 多态
概念
父类引用指向子类对象,从而产生多种形态。
二者具有直接或间接的继承关系时,父类引用可指向子类对象
前提
- 要有⼦⽗关系(决定了可以使⽤多态)
- 要有⽅法重写(决定了代码有没有实际意义)
- 要有⽗类引⽤指向⼦类对象
public class Father {
public void method(){
System.out.println("⽗类method⽅法");
}
}
//⼦类继承⽗类
public class Son extends Father {
//重写⽗类的⽅法
public void method(){
System.out.println("⼦类的method⽅法");
}
}
public static void main(String[] args) {
//⽗类类型的变量引⽤⼦类类型的实例对象
Father father = new Son();
father.method();
}
应用
场景一:
使用父类作为方法形参实现多态,使方法参数的类型更为宽泛。
public class Polymorphism {
public static void main(String[] args) {
Master master = new Master();
master.name = "jack";
//master.feed(new Dog());
Dog dog = new Dog();
master.feed(dog);
}
}
//主人类
class Master {
String name;
//将动物类直接作为参数
public void feed(Animal animal) {
System.out.println(name + "喂食");
//调用eat方法,至于是谁的eat就需要看feed从参数是谁,如果是dog就是dog的feed方法
animal.eat();
}
}
//动物类:父类
class Animal {
public void eat() {
System.out.println("吃......");
}
}
//狗狗类:子类
class Dog extends Animal {
public void eat() {
System.out.println("狗狗开始吃狗粮");
}
}
场景二:使用父类作为方法返回值实现多态,使方法可以返回不同子类对象
public class Polymorphism {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int choose = scanner.nextInt();
Master master = new Master();
//choose通过buy方法赋值type=choose,if会选择type实现实例化不同的子类。
// 如tyepe是1就会实例化 Animal animal =new Dog();然后返回给return animal;(返回的是类型,不是1)
// 在通过if判断是否是空。。。
Master.Animal animal = master.buy(choose);
if (animal != null) {
System.out.println("购买成功");
//animal不是空,通过instanceof判断animal类型
if (animal instanceof Master.Dog) {
//将animal赋值给dog,现在程序想调用子类方法,但是dog是Dog类,animal是Animal类,所以需要强转
Master.Dog dog = (Master.Dog) animal;
//调用Dog的eat();
dog.eat();
} else if (animal instanceof Master.Bird) {
Master.Bird bird = (Master.Bird) animal;
bird.eat();
}
} else {
System.out.println("购物失败");
}
}
}
//主人类
class Master {
public Animal buy(int type) {
Animal animal = null;
if (type == 1) {
animal = new Dog();
} else if (type == 2) {
animal = new Bird();
}
return animal;
}
//动物类:父类
class Animal {
public void eat() {
System.out.println("吃......");
}
}
//狗狗类:子类
class Dog extends Animal {
public void eat() {
System.out.println("狗狗开始吃狗粮");
}
}
class Bird extends Animal {
public void eat() {
System.out.println("鸟开始吃虫子");
}
}
}
成员访问的特点
-
成员变量
- 编译看左边(⽗类) , 运⾏看左边(⽗类) (静态绑定)
- ⼀般中情况使⽤极少, 成员变量⼀般都是私有化的
public class Father { String name = "⼩红"; } public class Son extends Father { String name = "⼩明"; } public static void main(String[] args) { Father father = new Son(); String str = father.name; System.out.println(str); //⼩红 }
-
成员方法
- 编译看左边(⽗类), 运⾏看右边(⼦类), 动态绑定
- ⼦类中如果对⽗类中的⽅法进⾏了重写, ⽗类中的⽅法⽆论如何都⽆法被访问, 这也是java动态
绑定的结果 - 动态绑定会选择最优执⾏⽅法, 只要⼦类重写了⽗类的⽅法, ⽆论在什么地⽅调⽤, 都会先找⼦
类
public class Father { public void method(){ System.out.println("⽗类method⽅法"); } } public class Son extends Father { public void method(){ System.out.println("⼦类method⽅法"); } } public static void main(String[] args) { Father father = new Son(); father.method();//⼦类method⽅法 }
-
静态方法
- 编译看左边(⽗类),运⾏看左边(⽗类)
public class Father { public static void method(){ System.out.println("⽗类method⽅法"); } } public class Son extends Father { public static void method(){ System.out.println("⼦类method⽅法"); } } public static void main(String[] args) { Father father = new Son(); father.method();//⽗类method⽅法 //Father.method(); }
拆箱和装箱
向上转型
- 将⼦类对象看作是⽗类类型, 也就是我们平时使⽤的多态的形式
- ⽆法调⽤⼦类特有的功能
向下转型
-
将⽗类引⽤指向的⼦类对象再转回⼦类类型
-
这种转型是有危险的, 因为是强制性的, ⼀旦转向的类型跟这个对象不匹配, 就会报错
java.lang.ClassCastException (类型转换异常, 属于运⾏时异常)public static void main(String[] args) { Father f = new Son(); //转回⼦类类型 Son son = f; // }
public static void main(String[] args) { //向上溯型 //父类 = 子类对象 Animal animal = new Cat(); animal.eating(); //向下溯型 // 子类 = (子类)父类对象 // Cat cat = (Cat) new Animal(); // 报错 //向下溯型容易出现一个异常 ClassCastException --类型转换异常 //避免这个异常: 先向上溯型,后向下溯型 Animal ani = new Dog(); Dog dog = (Dog) ani; // 可以成功执行了 //向下溯型的好处: 解决了多态的缺点, 可以使用子类的特有的属性和方法了 dog.yelling(); }
Father father = new Son();
father.method();//⽗类method⽅法
//Father.method();
}
拆箱和装箱
向上转型
- 将⼦类对象看作是⽗类类型, 也就是我们平时使⽤的多态的形式
- ⽆法调⽤⼦类特有的功能
向下转型
-
将⽗类引⽤指向的⼦类对象再转回⼦类类型
-
这种转型是有危险的, 因为是强制性的, ⼀旦转向的类型跟这个对象不匹配, 就会报错
java.lang.ClassCastException (类型转换异常, 属于运⾏时异常)public static void main(String[] args) { Father f = new Son(); //转回⼦类类型 Son son = f; // }
public static void main(String[] args) { //向上溯型 //父类 = 子类对象 Animal animal = new Cat(); animal.eating(); //向下溯型 // 子类 = (子类)父类对象 // Cat cat = (Cat) new Animal(); // 报错 //向下溯型容易出现一个异常 ClassCastException --类型转换异常 //避免这个异常: 先向上溯型,后向下溯型 Animal ani = new Dog(); Dog dog = (Dog) ani; // 可以成功执行了 //向下溯型的好处: 解决了多态的缺点, 可以使用子类的特有的属性和方法了 dog.yelling(); }