面向对象进阶
一、static
需求:写一个JavaBean类来描述这个班级的学生
属性:姓名、年龄、性别
行为:学习
package com_03_mianxaingduixiang.Demo01_Static;
public class Student {
private String name;
private int age;
private char gender;
public static String teacherName;
public Student() {
}
public Student(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
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 char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
//行为
public void study(){
System.out.println(name+"正在学习");
}
public void show(){
System.out.println(name+","+age+","+gender+","+teacherName);
}
}
package com_03_mianxaingduixiang.Demo01_Static;
public class StudentTest {
public static void main(String[] args) {
//static 修饰的可以在整个类里面共用
Student.teacherName = "kinbow"; //用类名调用
//1.创建第一个学生对象
Student s1 = new Student();
s1.setName("张三");
s1.setAge(23);
s1.setGender('男');
// s1.teacherName = "kinbow"; //用对象调用
s1.study();
s1.show();
//2,创建第二个学生对象
Student s2 = new Student();
s2.setName("李四");
s2.setAge(22);
s2.setGender('女');
s2.study();
s2.show();
}
}
1.Static
static 表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量
★被static修饰的成员变量,叫做静态变量
(1)特点:
①被该类所有对象共享
②不属于对象,属于类
③静态变量是随着类的加载而加载的,优先于对象出现的
(2)调用:
①类名调用(推荐)
②对象名调用
★被static修饰的成员方法,叫做静态方法
(1)特点:
①多用于测试类和工具类中
②JavaBean类很少会用
(2)调用:
① 存在于本类时,直接调用方法名
②(存在于其他类)类名调用(推荐)
③(存在于其他类)对象名调用
拓展:
(1) JavaBean类
用来描述一些事务的类。比如Student、Teacher、Dog、Cat等
(2) 测试类
用来检查其他的类是否书写正确,带有main方法的类,是程序的入口
(3)工具类:
帮助我们做一些事情,但不描述任何事物的类
★创建工具类的时候的注意事项
(1)类名要见名知意,如public class Math {…};
(2)私有化构造方法
public class ArrUtil{
private ArrUtil(){…}
}
为什么要私有化构造方法:
因为构造方法一旦私有,那么外界就不能创建这个类的对象了,因为创建这个类的对象没有任何意义
(3)方法定义为静态
工具类练习1
package com_03_mianxaingduixiang.Demo02_Static;
/*需求:
在实际开发中,经常会有到一些数组使用的工具类,请按照一下要求编写一个数组的工具类ArrayUtil
1.提供一个工具类方法printArr,用于返回整数数组的内容
返回的字符串格式如:[10,20,30,100](只考虑整数数组,且只考虑一维数组)
2.提供一个工具方法getAerage,用于返回平均分。(只考虑浮点型数组,且只考虑一维数组)
3.定义一个测试类TestDemo,调用该工具类的工具方法,并返回结果
*/
public class ArrayUtil {
//私有化构造方法
private ArrayUtil() {
}
//定义为静态的,方便调用
public static String printArr(int[] arr) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
//i表示索引 arr[i] 表示元素
if (i == arr.length - 1) {
sb.append(arr[i]);
} else {
sb.append(arr[i]).append(", ");
}
}
sb.append("]");
return sb.toString();
}
public static double getAerage(double[] arr) {
double sum = 0;
for (int i = 0; i < arr.length; i++) {
sum = sum + arr[i];
}
return sum/arr.length;
}
}
public class TestDemo {
public static void main(String[] args) {
//测试工具类的两个方法是否正确
int[] arr = {10, 20, 30, 40, 50, 88, 100};
String str = ArrayUtil.printArr(arr);
System.out.println(str);
double[] err = {23.0, 323.0, 4546.99, 5676, 4, 23};
double avg = ArrayUtil.getAerage(err);
System.out.println(avg);
}
}
工具类练习2
工具类练习2
public class Student {
private String name;
private int age;
private char gender;
public Student() {
}
public Student(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
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 char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
}
//需求:
///定义一个集合,用于存储三个学生对象
//学生的属性为:name,age,gender
//定义一个工具类,用于获取集合中最大学生的年龄
public class StudentTest {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
Student s1 = new Student("zhangsan",22,'男');
Student s2 = new Student("李四",278,'女');
Student s3 = new Student("王五",24,'女');
list.addAll(Arrays.asList(s1,s2,s3));
int maxAge = StudentUtil.getMaxAge(list);
System.out.println(maxAge);
}
}
package com_03_mianxaingduixiang.Demo03_StaticTool;
import java.util.ArrayList;
public class StudentUtil {
private StudentUtil(){
}
public static int getMaxAge(ArrayList<Student> list){
int maxAge = list.get(0).getAge();
for (int i = 1; i < list.size(); i++) {
Student stu = list.get(i);
if(stu.getAge()>maxAge){
maxAge = stu.getAge();
}
}
return maxAge;
}
}
static的注意事项
★静态方法只能访问静态变量和静态方法(静态只能访问静态)
★非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法(非静态可以访问所有)
★静态方法中没有this关键字
package com_03_mianxaingduixiang.Demo04_Static;
import jdk.dynalink.beans.StaticClass;
/*
从代码层面理解static
总结:
静态方法中没有this关键字
静态方法中,只能访问静态变量和静态方法
非静态方法可以访问所有
*/
public class Student {
String name;
int age;
static String teacherName;
//this:表示当前方法调用者的地址值
//这个this是由虚拟机赋值的
//谁调用这个方法,this指向谁
public void show1(Student this){
System.out.println(name+","+age+","+teacherName);
}
public static void method(){
System.out.println("静态方法");
}
}
package com_03_mianxaingduixiang.Demo04_Static;
public class StudentTest {
public static void main(String[] args) {
Student.teacherName = "kinbow" ;
Student s1 = new Student();
s1.name = "zhangsan";
s1.age = 22;
s1.show1();
System.out.println("================");
Student s2 = new Student();
s2.name = "lisi";
s2.age = 21;
s2.show1();
}
}
2.重新认识main方法
public static void main (String [ ] args)
★public :权限修饰符,被虚拟机 JVM 调用,访问权限足够大
★static :被虚拟机JVM调用,不用创建对象,直接类名访问,因为main方法是静态的,所有测试类 中其他方法也是需要静态的
★void : 表示方法的返回值,被JVM调用,不需要给JVM返回值
★main :程序主入口方法的名称,一个通用的名称,虽然不是关键字,但是被JVM识别
★String [ ] args:以前用于接收键盘录入数据的,现在没用
package com_03_mianxaingduixiang;
public class Deno05_MainMethod {
public static void main(String[] args) {
// [ ] :数组
// String: 数组的类型
// args :数组的名称
System.out.println(args.length);
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
二、继承
继承
★Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系
封装:
对象代表什么,就得封装对应的数据,并提供数据对应的行为
多态
1.继承
★Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系
public class Student extends Person{}
★Student 称为子类(派生类),Person称为父类(基类或超类)
2.使用继承的好处
(1)可以把多个子类中重复的代码抽取到父类中,提高代码的复用性
(2)子类可以在父类的基础上,增加其他的功能的,使子类更加强大
3.继承需要学习的要点
(1)如何设计自己的继承关系
①自己设计继承的注意点:
★什么时候要用到继承:当类跟类之间存在相同(共性)的内容时,并满足子类是父类的一种,就可以考虑使用继承,来优化代码
②继承需要学习的点
Ⅰ. 什么是继承,继承的好处
★继承是面向对象三大特征之一,可以让类跟类之间产生父子的关系
★把多个子类中重复的代码抽取到父类中,子类可以直接使用,减少代码冗余,提高复用性
Ⅱ . 继承的格式
★public class 子类 extends 父类 { };
Ⅲ . 继承后子类的特点?
★子类可以得到父类的属性和行为,子类可以使用
★子类可以在父类的基础上新增其他功能,子类更强大
★Java只支持单继承,不支持多继承,但支持多层继承
🔺单继承:一个子类只能继承一个父类
🔺多继承:子类不能同时继承多个父类
🔺多层继承:子类A继承父类B,父类B可以继承父类C(祖孙三代)
===》 C为A的 间 接 父类,B为A的 直 接 父类,C为B的直接父类
===》每一个类都直接或间接继承了Object类(祖宗类)
注意:写继承关系时不能用private私有型,否则子类将会无法访问,子类只能访问父类中非私有成员
Ⅳ . 子类能继承父类中的那些内容?(内存图/内存分析工具)
私有 private | 非私有 | |
---|---|---|
构造方法 | 不能继承 | 不能继承 |
成员变量 | 能继承但不可以使用(非要使用可以调用getter/setter方法) | 能继承也可以使用 |
成员方法 | 不能继承 | 不能继承但不能使用 |
构造方法的继承
//测试构造方法能否被子类继承
public class Test {
public static void main(String[] args) {
//利用空参构造创建子类对象
zi z1 = new zi();
//这里不报错的原因是:
//如果一个类中没有构造方法,那么虚拟机会自动的添加一个默认的空参构造
//利用带参构造创建子类对象
zi z2 = new zi("zhangsan",22);
// zi 类中没有带参构造方法
}
}
class Fu{
String name;
int age;
public Fu(){}
public Fu(String name,int age){
this.name = name;
this.age = age;
}
}
class zi extends Fu{
}
非私有成员变量的继承
私有成员变量的继承
成员方法的继承
只有父类中的虚方法才能被子类继承
虚方法:
只有 非private修饰 、 非static 修饰 、 非 final 修饰 的方法才叫虚方法
Ⅴ . 继承中:成员变量的访问特点
就近原则:谁离我近,我就用谁
1.继承中成员变量访问特点:就近原则
先在局部位置找,本类成员位置找,父类成员位置找,逐级往上
2.如果出现了重名的成员变量怎么办法?
System.out.println ( name ); ===>从局部位置往上找
System.out.println ( this.name ); ===>从本类成员位置开始往上找
System.out.println ( super.name );===>从父类成员位置开始往上找
package com_03_mianxaingduixiang.Demo09_Extends;
public class Test {
public static void main(String[] args) {
Zi3 z3 = new Zi3();
z3.show();
}
}
class Fu3{
String name = "Fu3";
String hobby = "喝茶";
}
class Zi3 extends Fu3{
String name = "Zi3";
String game = "吃鸡";
public void show(){
//如何打印 Zi3
System.out.println(name);
System.out.println(this.name);
//如何打印 Fu3
System.out.println(super.name);
//如何打印喝茶
System.out.println(hobby);
System.out.println(this.hobby);
System.out.println(super.hobby);
//如何打印吃鸡
System.out.println(game);
System.out.println(this.game);
}
}
Ⅵ . 继承中:成员方法的访问特点
直接调用满足就近原则:谁离我近,我就用谁
super调用,直接访问父类
package com_03_mianxaingduixiang.Demo_02_Extends.Demo10_Extends;
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.lunch();
}
}
class person{
public void eat(){
System.out.println("吃米饭,吃菜");
}
public void drink(){
System.out.println("喝开水");
}
}
class Student extends person{
public void lunch(){
//方法在调用的时候需要有调用者
//在方法名前面隐藏了 this.
//会先触发就近远侧
//1.先在本类(Student)中查看是否有此方法,如果有,直接调用方法
//2.如果没有,就会调用从父类(person)中继承下来的方法
eat();
drink();
//3.如果使用super调用,不会在子类中查找,会直接在父类(person)中查看并调用
super.eat();
super.drink();
}
}
方法的重写:
当父类的方法不也能满足子类现在的需求时,需要进行方法重写
书写格式
在继承体系中,子类出现了和父类一摸一样的方法声明,我们就称子类这个方法是重写的方法
@Override 重写注解
1.@Override是放在重写的方法上,校验子类重写时语法是否正确
2.加上注解后如果有红色波浪线,表示语法错误
3.建议重写方法都加 @Override注解,代码安全,优雅!
方法重写的本质
如果子类发生了方法重写,那么继承过来的虚方法会被子类重写的虚方法覆盖
方法重写的注意事项和要求
1.重写方法的名称、形参列表必须与父类中的一致
2.子类重写父类方法时,访问权限子类必须大于等于父类(空着不写<protected<public)
3.子类重写父类方法时,返回值类型必须小于等于父类
4.(建议)重写的方法尽可能和父类保持一致
5.私有方法不能重写
6.子类不能重写父类的静态方法,如果重写会报错的
7.只有添加到虚方法表里面的方法才能被重写
package com_03_mianxaingduixiang.Demo_02_Extends.Demo11_Extends_Override;
public class OverrideTest {
public static void main(String[] args) {
Student s = new Student();
overseasStudent ost = new overseasStudent();
s.lunch();
}
}
class person{
public void eat(){
System.out.println("吃米饭,吃菜");
}
public void drink(){
System.out.println("喝开水");
}
}
class overseasStudent extends person{
public void lunch(){
this.eat();
this.drink();
super.eat();
super.drink();
}
//应用场景:当父类中的方法不能满足子类现在的需求时,我们就需要把这个方法重写
//注意:子类中重写的方法上面需要加上 @Override注解
@Override
public void eat(){
System.out.println("吃米饭,吃菜");
}
@Override
public void drink(){
System.out.println("喝开水");
}
}
class Student extends person{
public void lunch(){
//方法在调用的时候需要有调用者
//在方法名前面隐藏了 this.
//会先触发就近远侧
//1.先在本类(Student)中查看是否有此方法,如果有,直接调用方法
//2.如果没有,就会调用从父类(person)中继承下来的方法
eat();
drink();
//3.如果使用super调用,不会在子类中查找,会直接在父类(person)中查看并调用
super.eat();
super.drink();
}
}
Ⅶ . 继承中:构造方法的特点
父类中的构造方法不会被子类继承
子类中所有的构造方法默认会先访问父类中的无参构造,再执行自己
为社么?
1.子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据
2.子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化
如何调用父类的构造方法
1.子类构造方法的第一行语句默认都是 super ( ),不写也存在,且必须在第一行
2.如果想调用父类有参构造,不许手动写super() 进行调用
特点
1.子类不能继承父类的构造方法,但是可以通过super调用
2.子类构造方法的第一行,有一个默认的 super();
3.默认先访问父类中无参构造的构造方法,再执行自己
4.如果想调用父类有参构造,必须手动书写
Ⅷ . this、super的使用总结
★this:理解为一个变量,表示当前方法调用者的地址值
public class Test{
public static void main(String []args){
Student s = new Student;
s.name = "zhangsan";
s.age = 23;
s.show();
//可以把对象在内存中的结构打印出来
ClassLayout layout = ClassLayout.parseInstance(s);
System.out.println(layout.toPrintable());
}
}
public class Student{
String name;
int age;
public void show(Student this){ //隐藏了Student this
System.out.println(this.name+","+this.age);
}
}
★super
代表父类存储空间
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.成员变量 访问本类成员变量 | this.成员方法 访问本类成员方法 | this.(…) 访问本类构造方法 |
super | super.成员变量 访问父类成员变量 | super.成员方法 访问父类成员方法 | super.(…) 访问父类构造方法 |
public class Test{
public static void main(String []args){
Student s = new Student;
}
}
public class Student{
String name;
int age;
String school = "清华大学";
public Student(){
//表示调用本类其他构造方法
//细节:虚拟机就不会再添加super();
this(name:null,age:0,school:"清华大学")
}
public Student(String name,int age,String school){
this.name = name;
this.age = age;
this.school = school;
}
}
★Extends练习
需求:
1.经理
成员变量:工号,姓名,工资,管理奖金
成员方法:工作(管理他人),吃饭(吃米饭)
2.厨师
成员变量:工号,姓名,工资
成员方法:工作(炒菜),吃饭(吃米饭)
package com_03_mianxaingduixiang.Demo_02_Extends.Demo15_Extends_JavaBean;
public class employee {
private String idnum;
private String name;
private double salsry;
public employee() {
}
public employee(String idnum, String name, double salsry) {
this.idnum = idnum;
this.name = name;
this.salsry = salsry;
}
public String getIdnum() {
return idnum;
}
public void setIdnum(String idnum) {
this.idnum = idnum;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalsry() {
return salsry;
}
public void setSalsry(double salsry) {
this.salsry = salsry;
}
public void work(){
System.out.println("员工在工作");
}
public void eat(){
System.out.println("员工在吃米饭");
}
}
package com_03_mianxaingduixiang.Demo_02_Extends.Demo15_Extends_JavaBean;
public class chef extends employee {
public chef() {
}
public chef(String idnum, String name, double salsry) {
super(idnum, name, salsry);
}
@Override
public void work() {
super.work();
System.out.println("厨师在炒菜");
}
}
package com_03_mianxaingduixiang.Demo_02_Extends.Demo15_Extends_JavaBean;
public class manager extends employee {
private double bouns;
//空参构造
public manager(){
}
//全参构造===》父类+子类
public manager(String idnum, String name, double salsry, double bouns) {
super(idnum, name, salsry);
this.bouns = bouns;
}
public double getBouns() {
return bouns;
}
public void setBouns(double bouns) {
this.bouns = bouns;
}
@Override
public void work(){
System.out.println("经理在管理员工");
}
}
package com_03_mianxaingduixiang.Demo_02_Extends.Demo15_Extends_JavaBean;
/*
需求:
1.经理
成员变量:工号,姓名,工资,管理奖金
成员方法:工作(管理他人),吃饭(吃米饭)
2.厨师
成员变量:工号,姓名,工资
成员方法:工作(炒菜),吃饭(吃米饭)
*/
public class employeeTest {
public static void main(String[] args) {
chef c = new chef("123","zhangsan",4400);
manager m = new manager("003","lisi",6500,3200);
System.out.println(c.getIdnum()+","+c.getName()+","+c.getSalsry());
c.work();
c.eat();
System.out.println("-----------------------");
System.out.println(m.getIdnum()+","+m.getName()+","+m.getSalsry()+","+m.getBouns());
m.eat();
m.work();
}
}
三、多态(面向对象三大特征之一)
封装:对象代表什么,就得分装什么样的数据,并提供数据对应的行为
有了封装,才有了面向对象三大特征
==多态:==指对象的多种形态
//对象的不同形态
Student s = new Student();
Peson p = new Student();
多态的应用场景:
在学生管理系统中的注册方法中可以将参数设成类似父类的管理人员
即 Person p = new Student();
Person p = new Teacher();
Person p = new adnminStrator();
还可以根据传递对象的不同,调用不同的方法
1.多态
(1)什么是多态?
同类型的对象,表现出的不同形态
(2)多态的表现形式
父类类型 对象名称 = 子类对象();
(3)多态的前提
★需要有 继承 / 实现 关系
★有父类引用指向子类对象 Fu f = new Zi();
★需要有方法重写
(4)多态的好处
使用父类型作为参数,可以接收所有子类对象
体现多态的扩展性与便利
package com_03_mianxaingduixiang.Demo_02_Extends.Demo15_Extends_JavaBean;
/*
需求:
1.经理
成员变量:工号,姓名,工资,管理奖金
成员方法:工作(管理他人),吃饭(吃米饭)
2.厨师
成员变量:工号,姓名,工资
成员方法:工作(炒菜),吃饭(吃米饭)
*/
public class employeeTest {
public static void main(String[] args) {
chef c = new chef("123","zhangsan",4400);
manager m = new manager("003","lisi",6500,3200);
System.out.println(c.getIdnum()+","+c.getName()+","+c.getSalsry());
c.work();
c.eat();
System.out.println("-----------------------");
System.out.println(m.getIdnum()+","+m.getName()+","+m.getSalsry()+","+m.getBouns());
m.eat();
m.work();
}
}
package com_03_mianxaingduixiang.Demo_03_polymorphic.polymorphic_test01;
public class Adminstrator extends Person {
@Override
public void show() {
System.out.println("管理员信息为:"+getName()+","+getAge());
}
}
package com_03_mianxaingduixiang.Demo_03_polymorphic.polymorphic_test01;
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:"+name+","+"age:"+age);
}
}
package com_03_mianxaingduixiang.Demo_03_polymorphic.polymorphic_test01;
public class Student extends Person{
@Override
public void show() {
System.out.println("学生信息为:"+getName()+","+getAge());
}
}
package com_03_mianxaingduixiang.Demo_03_polymorphic.polymorphic_test01;
public class Teacher extends Person {
@Override
public void show() {
System.out.println("老师信息为:"+getName()+","+getAge());
}
}
2.多态的特点
(1)多态调用成员的特点
①变量调用:
编译看左边,运行也看左边
②方法调用:
编译看左边,运行看右边
package com_03_mianxaingduixiang.Demo_03_polymorphic.polymorphic_test02;
import javax.xml.transform.Source;
public class Test {
public static void main(String[] args) {
//创建对象(用多态方式)
Aniaml a1 = new Dog();
Aniaml a2 = new Cat();
//调用变量 编译看左边,运行看左边
//编译看左边:javac在运行的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有,编译失败
//运行看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
System.out.println(a1.name); //动物;
System.out.println(a2.name); //动物
//调用方法 编译看左边,运行看右边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有,则编译失败
//运行看右边:java运行代码的时候,实际上运行的是子类中的方法
a1.show(); //Dog-----show方法
a2.show(); //Cat-----show方法
//理解
// Animal a = new Dog;
//现在是用 a 去调用调用变量和方法
//而 a 是Animal类型的,所以都会从Animal这个类中去找
//成员变量:在子类的对象中,会把父类的成员变量也继承下的
//成员方法:如果子类对方法进行重写,那么虚方法表中会把父类的方法进行覆盖的
}
}
class Aniaml {
String name = "动物";
public void show(){
System.out.println("Animal----show方法");
}
}
class Dog extends Aniaml {
String name = "狗";
@Override
public void show() {
System.out.println("Dog----show方法");
}
}
class Cat extends Aniaml {
String name = "猫";
@Override
public void show() {
System.out.println("Cat----show方法");
}
}</u>
3.多态的优势和弊端
(1)优势
①在多态形势下,右边对象可以实现解耦合,便于扩展和维护
Person p = new Student();
p.work(); //业务逻辑发生改变时,后续代码无需改变
②定义方法的时候,使用父类型作为参数,可以接受所有子类对象,体现多态的扩展性与便利
Person p = new Person();
StringBuilder sb = new StringBuilder();
ArrayList<Person> list = new ArrayList<>();
sb.append(p);
list.add(p);
(2)多态的弊端
①不能调用子类的特有功能
当调用成员方法的时候,编译看左边,运行看右边
那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错
(3)解决方案
将调用者变回子类类型就可以了
★自动类型转换
★强制类型转换
Animal a = new Dog();
//在转换的时候不能乱转,如果转成其他类型的,就会报错
//==》Dog a = (Cat)a;(×)
//运行报错:ClassCastException Create breakpoint: cannot be cast to class cat
Dog a = (Dog) a;
d.lookHome();
在Java中,会有判断当前变量类型的方法 intanceof
//if (变量名 intanceof 类型)
if(a intanceof dog){
dog c = (dog)a;
d.lookhome();
}else if{
cat c = (cat)a;
c.catchMouse();
}
但在Java jdk14之后有了新特性:将类型判断和类型强制转换合在一起
//即if(变量名 instanceof 类型 新变量名){}
//表示:先判断变量 a 是否为此类型的,如果是,则强转成此类型,创建一个新的变量名
//如果不是,则不进行强转,结果直接为false
if(a intanceof dog d){
d.lookhome();
}else if(a instanceof cat c){
c.catchMouse();
}
强制类型转换能解决什么问题
★可以转换成真正的子类型,从而调用子类的独有功能
★转换类型与真实对象类型不一致会报错
★转换的时候用 intanceof 关键字进行判断
4.多态的综合练习
需求:
根据需求完成代码:
1.定义狗类
属性:
年龄,颜色
行为:
eat(String something)(something表示吃的东西)
看家lookHome方法(无参数)
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 somethind)方法打印内容如下:
年龄为30岁的老王养了一只黑颜色的2岁的狗
2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
keepPet(Cat cat,String somethind)方法打印内容如下:
年龄为25岁的老李养了一只灰颜色的3岁的猫
3岁的灰颜色的猫眯着眼睛侧着头吃鱼
5.思考:
1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keepPet方法,过于繁琐,能否简化,并体会简化后的好处?
2.Dog和Cat虽然都是Animal的子类,但是都有其特有方法,能否想办法在keepPet中调用特有方法?
//父类
package com_03_mianxaingduixiang.Demo_03_polymorphic.polymorphic_test03;
public class Animal {
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("动物在吃东西");
}
}
//狗
package com_03_mianxaingduixiang.Demo_03_polymorphic.polymorphic_test03;
public class Dog extends Animal{
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge()+"岁的"+getColor()+"颜色的狗两只前腿死死抱住"+something+"猛吃");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
//猫
package com_03_mianxaingduixiang.Demo_03_polymorphic.polymorphic_test03;
public class Cat extends Animal {
public Cat() {
}
public Cat(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge()+"岁的"+getColor()+"颜色的猫眯着眼睛侧着头吃"+something);
}
public void catchMouse(){
System.out.println("猫在抓老鼠");
}
}
//饲养员
package com_03_mianxaingduixiang.Demo_03_polymorphic.polymorphic_test03;
import java.security.PublicKey;
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 keepPet(Cat cat,String something){
// System.out.println("年龄"+age+"的"+name+"养了一只"+cat.getColor()+"的"+cat.getAge()+"的猫");
// cat.eat(something );
// }
// public void keepPet(Dog dog,String somrthing){
// System.out.println("年龄"+age+"的"+name+"养了一只"+dog.getColor()+"的"+dog.getAge()+"的狗");
// dog.eat(somrthing);
// }
//写一个方法,能接收所有动物
//方法的形参可以写这些类的父类
public void keepPet(Animal an,String somrthing){
if(an instanceof Dog ){
Dog d = (Dog) an;
System.out.println("年龄"+age+"的"+name+"养了一只"+an.getColor()+"的"+an.getAge()+"的狗");
d.eat(somrthing);
}else if(an instanceof Cat){
Cat c = (Cat)an;
System.out.println("年龄"+age+"的"+name+"养了一只"+an.getColor()+"的"+an.getAge()+"的猫");
c.eat(somrthing);
}
}
}
//测试类
package com_03_mianxaingduixiang.Demo_03_polymorphic.polymorphic_test03;
public class Test {
public static void main(String[] args) {
Person p = new Person("老王",32);
Dog d = new Dog(3,"黄色");
Cat c = new Cat(2,"白色");
p.keepPet(d,"骨头");
p.keepPet(c,"鱼");
}
}
四、多态包、final、权限修饰符、代码块
1.包
(1)什么是包
包就是文件夹。用来管理各种不同功能的Java类,方便后期代码维护
(2)包名的规则
公司域名反写+包的作用,需要全部英文小写,见名知意
例如:www.baidu.com=====》com.baidu.domain
全类名(全限定名):包名+类名===> Student类:com.baidu.domain.Student
(3)使用其他类的规则
使用其它类时,需要使用全类名
public class Test{
public static void main(String []args){
//使用全类名
com.baidu.domain.Student s = new com.baidu.domain.Student();
}
}
但上面这种写法太过繁琐,所以直接在上面将包导进来即可
import com.baidu.domain.Student;
(1)导包的规则
①使用同一个包中的类时,不需要导包
②使用java.lang包中的类时,不需要导包
③其他情况都需要导包
④如果同时使用两个包中的同名类,需要使用全名类
package com_03_mianxaingduixiang.Demo_04_import.import_test01;
import com_03_mianxaingduixiang.Demo_04_import.Test.Teacher;
public class Test {
public static void main(String[] args) {
//1.使用同一个包中的类时不需要导包
Student s = new Student();
s.setName("zhangsan");
s.setAge(22);
System.out.println(s.getName()+","+s.getAge());
//2.使用java.lang中的类时也不需要导包
String str = "abc";
System.out.println(str);
//3.使用不同包下的类需要导包
Teacher t = new Teacher(); //在第3行进行了导包
t.setName("lisi");
t.setAge(34);
System.out.println(t.getName()+","+t.getAge());
//4.如果同时使用两个包中的同名类,需要使用全类名
com_03_mianxaingduixiang.Demo_04_import.import_test01.Student stu1 = new com_03_mianxaingduixiang.Demo_04_import.import_test01.Student();
com_03_mianxaingduixiang.Demo_04_import.Test.Student st2 = new com_03_mianxaingduixiang.Demo_04_import.Test.Student();
}
}
2.final关键字
final:最终的,不可改变的
final可以修饰那些东西
①方法 ②类 ③变量:
(1)final修饰方法
表名该方法是最终方法,不能被重写
final class fu{
public final void show(){
System.out.println("父类的show方法");
}
}
class zi extends fu{
@Override
public void show(){ //会报错,final修饰的方法不能被重写,是最终方法
System.out.println("子类的show方法");
}
}
(2)final修饰类
表明该类是最终类,不能被继承
final class fu{
public final void show(){
System.out.println("父类的show方法");
}
}
class zi extends fu{ //会报错,不能继承fu这个类,被final修饰的方法不能被继承
@Override
public void show(){
System.out.println("子类的show方法");
}
}
(3)final修饰变量
叫做常量,只能被赋值一次
final int a = 10; //这里必须要赋值,不赋值会报错
System.out.println(a); //10
a = 20; //会报错,a不能被再次赋值
①常量:
爱实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性
②常量的命名规则
★单个单词:全部大写
★多个单词:全部大写,单词与单词之间用下划线隔开,例如 world_cup
③细节:
★final修饰的变量是基本数据类型:那么变量存储的数据值不能发生改变
★final修饰的变量是引用数据类型:那么变量存储的地址值不能发生改变,对象内部的可以改变
//★final修饰的变量是基本数据类型:那么变量存储的数据值不能发生改变
final double pi = 3.14; //赋完值不可以再改变
System.out.println(pi);
//★final修饰的变量是引用数据类型:那么变量存储的地址值不能发生改变,对象内部的可以改变
final Student S = new Student(); //地址值已经固定,不能再改变
S.setName("张三"); S.setAge(22); //但里面的内容可以赋值
System.out.println(S.getName()+","+S.getAge());
final int []ARR = {1,2,3,4,5};//地址值已经固定
ARR[0] = 4; ARR[1] = 4;ARR[2] = 4;ARR[3] = 4;ARR[4] = 4; //但是数组里面的内容可以改变
for (int i = 0; i < ARR.length; i++) {
System.out.println(ARR[i]);
}
(4)final练习
package com_03_mianxaingduixiang.Demo_05_final.Demo02_final_test02;
import javax.swing.*;
import javax.swing.plaf.BorderUIResource;
import java.util.ArrayList;
import java.util.Scanner;
//需求
//将学生管理系统中用户的操作改写为常量的形式
public class test {
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 SELECT_STUDENT = "4";
private static final String EXIT= "5";
public static void main(String[] args) {
ArrayList<String>list = new ArrayList();
System.out.println("-----------欢迎来到学生管理系统----------");
System.out.println("1.添加学生");
System.out.println("2.删除学生");
System.out.println("3.修改学生");
System.out.println("4.查询学生");
System.out.println("5.退出");
Scanner sc = new Scanner(System.in);
String choose = sc.next();
switch (choose){
case ADD_STUDENT -> add_Student(list);
case DELETE_STUDENT -> deltet_Student(list);
case UPDATE_STUDENT -> update_Student(list);
case SELECT_STUDENT -> select_Student(list);
case EXIT -> {
System.out.println("----------退出----------");
}
}
}
public static void add_Student(ArrayList<String>list){}
public static void deltet_Student(ArrayList<String>list){}
public static void update_Student(ArrayList<String>list){}
public static void select_Student(ArrayList<String>list){}
public static void queryStudent(ArrayList<String>list){}
}
3.权限修饰符
(1)什么是权限修饰符
是用来控制一个成员能够被访问的范围的
可以修饰成员变量,方法啊,构造方法,内部类
有四种作用范围由小到大: private < 空着不写 (缺省/默认)< protected < public
修饰符 | 同一个类中 | 同一个包中其他类 | 不同包下的子类 | 不同包下的无关类 |
---|---|---|---|---|
private | √ | |||
空着不写 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
★private:相当于私房钱,只能自己用
★默认:只能在本包中使用(自己家里面的东西,只能自己家里人用)
★protected:受保护的,不同包下的子类可以使用
★public:公共的(所有人都可以使用)
2)权限修饰符的使用规则
实际开发中,一般只用 public 和 private,即遵循一下规则
★成员变量私有private
★方法公开public
特殊:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有
例如:集合底层的容量扩容方法
4.代码块
(1)局部代码块
书写在 方法内的代码(只在所属的方法{}里面的代码):提前结束变量的生命周期
public class Test{
public static void main (String []args){
{
int a = 10; //{}里面的代码块就是局部代码块。
}
System.out.println(a);//变量运行完就会在内存中释放掉
}
}
(2)构造代码块
什么是构造代码块:
①写在成员位置的代码块
②作用:可以把多个构造方法中重复的代码抽取出来
③执行时机:我们在创建本类对象的时候会先执行构造代码块再执行构造方法
public class Student{
private String name;
private int age;
{
System.out.println("开始创建对象了") //yu
}
public Student(){}
public Student(String name,int age){}
}
但是这种方法不够灵活,所以可以把重复代码卸载防高方法里面,再使用this关键字调用有参构造
public class Student{
private String name;
private int age;
{
System.out.println("开始创建对象了")
}
public Student(){
this(null,0);
}
public Student(String name,int age){
this.name = name;
this.age = age;
}
}
或者将重复的代码写成一个新的方法体
public class Student{
private String name;
private int age;
public void 新方法{
重复的代码体
}
public Student(){
调用方法();
}
public Student(String name,int age){
调用方法();
this.name = name;
this.age = age;
}
}
(3)静态代码块
①格式:static {代码体};
②特点:需要通过static关键字进行修饰,随着类的加载而加载,并且自动触发,只执行一次
③使用场景:在类加载的时候,做一些数据初始化的时候使用
package com_03_mianxaingduixiang.Demo_05_final.Demo04_Static_codeBlock;
public class Student {
private String name;
private int age;
//执行时机:随着类的加载而加载,并且只执行一次
// 当类被加载到内存中时会自动执行
//在初始化一些信息的时候会用到
static {
System.out.println("静态代码块执行了");
}
public Student() {
System.out.println("空参构造");
}
public Student(String name, int age) {
System.out.println("有参构造");
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;
}
}
(4)代码块总结
①代码块的分类:
局部代码块、构造代码块、静态代码块
②局部代码块的作用
提前结束代码的生命周期(已经淘汰)
③构造代码块的而作用
抽取构造代码块中的重复代码(不够灵活)
④静态代码块的作用
数据的初始化(重点)