封装:对象代表什么,就得封装对应的数据,并提供数据对应的行为。
一、多态:对象的多种形态。同类型的对象,表现出的不同形态。
1.多态的表现形式:父类类型 对象名称 = 子类对象;
学生形态 对象
Student s = new Student( );
人的形态 对象
Person p = new Student( );
2.多态的前提:
1)有继承/实现关系。
2)有父类引用指向子类对象。Fu f = new Zi( );
3)有方法的重写。
3.多态的优势:
1)在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
Person p= new Student( );
p.work( ); //业务逻辑发生改变时,后续代码无需修改。
2)★定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利。
4.多态的弊端:不能调用子类的特有功能。
1)解决方法:变回子类类型。如果转成其他类类型,会报错。
2)引用数据类型的类型转换,有2种:自动类型转换;强制类型转换。
Person p = new Student(); //自动类型转换
Student s = (Student) p; //强制类型转换
3)强制类型转换,可以转换成真正的子类类型,从而调用子类独有功能。
转换类型与真实对象类型不一致会报错。
转换的时候用instanceof关键字进行判断。
4)代码:
//多态弊端
public class Test {
public static void main(String[] args) {
//创建对象
Animal a = new Dog();
//编译看左边,运行看右边
a.eat();
//多态的弊端
//不能调用子类的特有功能
//当调用成员方法的时候,编译看左边,运行看右边
//在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错
//a.housekeeping();
//多态弊端解决方法:把调用者a再变回子类类型
/*int b = 10;
byte c = (byte) b;*/
Dog d = (Dog) a;
d.housekeeping();
//细节:转换时候不能转换成随意类型,否则报错
/*Cat c = (Cat) a;
c.catchMouse();*/
//实际开发中需要先判断类型是否可以转换
/*if (a 是不是 狗){
Dog d = (Dog) a;
}else if (a 是不是 猫){
Cat c = (Cat) a;
}*/
/*if (a instanceof Dog) {
Dog dog = (Dog) a;
dog.housekeeping();
} else if (a instanceof Cat) {
Cat cat = (Cat) a;
cat.catchMouse();
} else {
System.out.println("没有这个类型,无法转换");
}*/
//★★★JDK14新特性
//先判断a是否为Dog类型,如果是则强转成Dog类型,转换后变量名为dog
//如果a不是Dog类型,结果直接为false
if (a instanceof Dog dog) {
dog.housekeeping();
} else if (a instanceof Cat cat) {
cat.catchMouse();
} else {
System.out.println("没有这个类型,无法转换");
}
}
}
class Animal {
public void eat() {
System.out.println("动物在吃东西");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
public void housekeeping() {
System.out.println("狗在看家");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃小鱼干");
}
public void catchMouse() {
System.out.println("猫在抓老鼠");
}
}
5.多态的应用场景:
1)根据传递对象的不同,调用不同的show方法。
2)代码:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show() {
System.out.println(name + "," + age);
}
}
public class Administrator extends Person {
@Override
public void show() {
System.out.println("管理员的信息为" + getName() + "," + getAge());
}
}
public class Student extends Person {
@Override
public void show() {
System.out.println("学生的信息为" + getName() + "," + getAge());
}
}
public class Teacher extends Person {
@Override
public void show() {
System.out.println("老师的信息为" + getName() + "," + getAge());
}
}
public class Test {
public static void main(String[] args) {
//创建三个对象,并调用register方法
Student s = new Student();
s.setName("皓恒");
s.setAge(20);
Teacher t = new Teacher();
t.setName("瑞胤");
t.setAge(30);
Administrator admin = new Administrator();
admin.setName("管理员");
admin.setAge(35);
register(s);//学生的信息为皓恒,20
register(t);//老师的信息为瑞胤,30
register(admin);//管理员的信息为管理员,35
}
//这个方法既能接收老师,又能接收学生,还能接收管理员
//只能把参数写成这三个类型的父类
public static void register(Person p) {
p.show();
}
}
6.多态调用成员的特点:
1)变量调用:编译看左边,运行也看左边。
2)方法调用:编译看左边,运行看右边。
3)代码:
public class Test {
public static void main(String[] args) {
//多态方式创建对象
//Fu f = new Zi();
Animal a = new Dog();
//调用成员变量:编译看左边,运行也看左边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有编译成功,否则编译失败
//运行也看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
//理解:用a去调用变量和方法,a是Animal类型,默认都会从Animal这个类去找
//成员变量:在子类的对象中,会把父类的成员变量也继承下来
System.out.println(a.name);//动物
//调用成员方法
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有编译成功,否则编译失败
//运行看右边:java运行代码的时候,实际上运行的是子类中的方法
//成员方法:如果子类对方法进行了重写,在虚方法表里会把父类的方法进行覆盖
a.show();//Dog --- show方法
}
}
class Animal {
String name = "动物";
public void show() {
System.out.println("Animal --- show方法");
}
}
class Dog extends Animal {
String name = "狗";
@Override
public void show() {
System.out.println("Dog --- show方法");
}
}
class Cat extends Animal {
String name = "猫";
@Override
public void show() {
System.out.println("Cat --- show方法");
}
}
7.多态的综合练习
public class Animal {
/*属性:
年龄,颜色
行为:
eat(String something)(something表示吃的东西)*/
private int age;
private String color;
public Animal() {
}
public Animal(int age, String color) {
this.age = age;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void eat(String something) {
System.out.println("动物在吃" + something);
}
}
public class Cat extends Animal {
public Cat() {
}
public Cat(int age, String color) {
super(age, color);
}
/*行为:
eat(String something)方法(something表示吃的东西)
逮老鼠catchMouse方法(无参数)*/
@Override
public void eat(String something) {
System.out.println(getColor() + getAge() + "岁的猫狼吞虎咽" + something);
}
public void catchMouse() {
System.out.println("猫抓老鼠");
}
}
public class Dog extends Animal {
/*1.定义狗类
行为:
eat(String something)(something表示吃的东西)
看家housekeeping方法(无参数)*/
//空参构造
//带全部参数的构造
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
/*行为:
eat(String something)(something表示吃的东西)
看家housekeeping方法(无参数)*/
@Override
public void eat(String something) {
System.out.println(getAge() + "岁" + getColor() + "的伯恩山犬懒洋洋地躺在靠椅上吃" + something);
}
public void housekeeping() {
System.out.println("狗在看家");
}
}
public class Person {
/*属性:
姓名,年龄
行为:
keepPet(Dog dog,String something)方法
功能:喂养宠物狗,something表示喂养的东西
行为:
keepPet(Cat cat,String something)方法
功能:喂养宠物猫,something表示喂养的东西
生成空参有参构造,set和get方法
定义测试类(完成以下打印效果):
keepPet(Dog dog,String something)方法打印内容如下:
年龄为30岁的饲养员养了一只黑色的3岁的伯恩山犬 // 饲养员的行为
3岁黑色的伯恩山犬懒洋洋地躺在靠椅上吃牛肉 //狗的行为
keepPet(Cat cat,String something)方法打印内容如下:
年龄为35岁的铲屎官养了一只棕色的6岁的猫
棕色6岁的猫狼吞虎咽三文鱼罐头*/
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/*keepPet(Dog dog,String something)方法打印内容如下:
年龄为30岁的饲养员养了一只黑色的3岁的伯恩山犬 // 饲养员的行为
3岁黑色的伯恩山犬懒洋洋地躺在靠椅上吃牛肉 //狗的行为
keepPet(Cat cat,String something)方法打印内容如下:
年龄为35岁的铲屎官养了一只棕色的6岁的猫
棕色6岁的猫狼吞虎咽三文鱼罐头*/
/*//饲养狗
public void keepPet(Dog dog, String something) {
System.out.println("年龄为" + getAge() + "岁的" + getName() +
"养了一只" + dog.getColor() + "的" + dog.getAge() + "岁的伯恩山犬");
dog.eat(something);
}
//饲养猫
public void keepPet(Cat cat, String something) {
System.out.println("年龄为" + getAge() + "岁的" + getName() +
"养了一只" + cat.getColor() + "的" + cat.getAge() + "岁的猫");
cat.eat(something);
}*/
//生成一个方法,能接收所有的动物
//方法的形参,可以写这些类的父类Animal
public void keepPet(Animal a, String something) {
if (a instanceof Dog d) {
System.out.println("年龄为" + getAge() + "岁的" + getName() +
"养了一只" + d.getColor() + "的" + d.getAge() + "岁的伯恩山犬");
d.eat(something);
} else if (a instanceof Cat c) {
System.out.println("年龄为" + getAge() + "岁的" + getName() +
"养了一只" + c.getColor() + "的" + c.getAge() + "岁的猫");
c.eat(something);
} else {
System.out.println("没有这种动物");
}
}
}
/*根据需求完成代码:
1.定义狗类
属性:
年龄,颜色
行为:
eat(String something)(something表示吃的东西)
看家housekeeping方法(无参数)
2.定义猫类
属性:
年龄,颜色
行为:
eat(String something)方法(something表示吃的东西)
逮老鼠catchMouse方法(无参数)
3.定义Person类//饲养员
属性:
姓名,年龄
行为:
keepPet(Dog dog,String something)方法
功能:喂养宠物狗,something表示喂养的东西
行为:
keepPet(Cat cat,String something)方法
功能:喂养宠物猫,something表示喂养的东西
生成空参有参构造,set和get方法
4.定义测试类(完成以下打印效果):
keepPet(Dog dog,String something)方法打印内容如下:
年龄为30岁的饲养员养了一只黑色的3岁的伯恩山犬 // 饲养员的行为
3岁黑色的伯恩山犬懒洋洋地躺在靠椅上吃牛肉 //狗的行为
keepPet(Cat cat,String something)方法打印内容如下:
年龄为35岁的铲屎官养了一只棕色的6岁的猫
棕色6岁的猫狼吞虎咽三文鱼罐头
5.思考:
1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keepPet方法,过于繁琐,能否简化,并体会简化后的好处?
2.Dog和Cat虽然都是Animal的子类,但是都有其特有方法,能否想办法在keepPet中调用特有方法?*/
public class Test {
public static void main(String[] args) {
/*//创建对象并调用方法
Person p1 = new Person("饲养员", 30);
Dog d = new Dog(3, "黑色");
p1.keepPet(d, "牛肉");
Person p2 = new Person("铲屎官", 35);
Cat c = new Cat(6, "棕色");
p2.keepPet(c, "三文鱼罐头");*/
//创建饲养员的对象
Person p = new Person("饲养员", 30);
Dog d = new Dog(3, "黑色");
Cat c = new Cat(6, "棕色");
p.keepPet(d, "牛肉");
p.keepPet(c, "三文鱼罐头");
}
}
二、包
1.作用:
1)包就是文件夹。
2)用来管理各种不同功能的Java类,方便后期代码维护。
2.包名的规则:公司域名反写+包的作用。
需要全部英文小写,见名知意。com.company.domain
package com.company.domain
public class Student{
私有化成员变量
构造方法
成员方法
}
3.全类名/全限定名:包名+类名
com.company.domain.Student
4.使用其他类的规则
1)推演:
使用其他类时,需要使用全类名。
public class Test {
public static void main(String[] args) {
com.company.domain.Student s = new com.company.domain.Student();
}
}
//导包
import com.company.domain.Student;
public class Test {
public static void main(String[] args) {
Student s = new Student();
}
}
2)使用同一个包中的类时,不需要导包。
3)使用java.lang包中的类时,不需要导包。
4)其他情况都需要导包。
5)如果同时使用两个包中的同名类,需要用全类名。
三、final:最终的,不可被改变的。
1.final修饰方法(了解):表明该方法是最终方法,不能被重写。
应用:不想被改变的规则方法。
2.final修饰类(了解):表明该类是最终类,不能被继承。
3.final修饰变量(掌握):叫做常量,只能被赋值一次。
1)常量:实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性。
2)常量的命名规范:
单个单词:全部大写。
多个单词:全部大写,单词之间用下划线隔开。
3)★★★细节:
final修饰的变量是基本数据类型:变量存储的数据值不能发生改变。
final修饰的变量是引用数据类型:变量存储的地址值不能发生改变,对象内部的属性值可以改变。
4)核心:
常量记录的数据是不能发生改变的。
5)字符串是不可变的(private,final)。
6)常量的练习:
将学生管理系统中用户的操作改写为常量的形式。
Project Structure–Modules–Import Module–包里的iml配置文件
代码:
import java.util.ArrayList;
import java.util.Scanner;
public class StudentSystem {
private static final String ADD_STUDENT = "1";
private static final String DELETE_STUDENT = "2";
private static final String UPDATE_STUDENT = "3";
private static final String QUERY_STUDENT = "4";
private static final String EXIT = "5";
public static void startStudentSystem() {
ArrayList<Student> list = new ArrayList<>();
loop:
while (true) {
System.out.println("------------欢迎来到学生管理系统-------------");
System.out.println("1:添加学生");
System.out.println("2:删除学生");
System.out.println("3:修改学生");
System.out.println("4:查询学生");
System.out.println("5:退出");
System.out.println("请输入您的选择:");
Scanner sc = new Scanner(System.in);
String choose = sc.next();
switch (choose) {
case ADD_STUDENT -> addStudent(list);
case DELETE_STUDENT -> deleteStudent(list);
case UPDATE_STUDENT -> updateStudent(list);
case QUERY_STUDENT -> queryStudent(list);
case EXIT -> {
System.out.println("退出");
System.exit(0);
}
default -> System.out.println("没有这个选项");
}
}
}
}
四、权限修饰符
1.用来控制一个成员能够被访问的范围。
2.可以修饰成员变量,方法,构造方法,内部类。
3.有四种,作用范围由小到大(private < 空着不写(缺省/默认) < protected < public)
4.权限修饰符的使用规则:
1)实际开发中,一般只用private和public。
成员变量私有。方法公开。
2)特例:如果方法中的代码是抽取其他方法中的共性代码,这个方法一般也私有。
五、代码块
1.局部代码块(淘汰)
1)写在方法里的单独的大括号。节约内存。
2)提前结束变量的生命周期(已淘汰)。
public class Test {
public static void main(String[] args) {
//局部代码块(节约内存,淘汰)
{
int a = 10;
}//当代码执行到这里时,变量a就从内存中消失了
System.out.println(a);
}
}
2.构造代码块(逐渐淘汰)
1)写在成员位置的代码块。
2)作用:可以把多个构造方法中重复的代码抽取出来(不够灵活)。
3)执行时机:我们在创建本类对象的时候会先执行构造代码块再执行构造方法。
3.静态代码块(掌握)
1)格式:static{}
2)特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次。
3)使用场景:在类加载的时候,做一些数据初始化的时候使用(重点)。
4.代码:
public class Student {
private String name;
private int age;
//执行时机:
//随着类的加载而加载的,并且只执行一次
static {
System.out.println("静态代码块执行了");
}
/* //构造代码块
{
System.out.println("开始创建对象了");
}
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}*/
//构造方法里有重复
/* //第一种方法
public Student() {
//调用本类其他构造
this(null,0);
}
public Student(String name, int age) {
System.out.println("开始创建对象了");
this.name = name;
this.age = age;
}*/
//第二种方法
public Student() {
//调用方法
}
public Student(String name, int age) {
//调用方法
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
import java.util.ArrayList;
import java.util.Scanner;
public class App {
//静态代码块,初始化
static ArrayList<User> list = new ArrayList<>();
static {
//添加一些用户信息
list.add("川研", "123", "312123505009081234", "12345677890");
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("欢迎来到学生管理系统");
System.out.println("请选择操作:1.登录 2.注册 3.忘记密码");
String choose = sc.next();
switch (choose) {
case "1" -> login(list);
case "2" -> register(list);
case "3" -> forgetPassword(list);
case "4" -> {
System.out.println("谢谢使用,再见");
System.exit(0);
}//大括号不能省略
default -> System.out.println("没有这个选项");
}
}
}
}