Day10_面向对象进阶

Day10_面向对象进阶

今日的任务

  • static关键字:开发中如何定义一个共享的信息,给所有对象共享访问。
  • 设计模式:单例:有些类只需要一个对象就可以了,如任务管理器对象,如何实现一个类只能对外产生同一个对象。
  • 面向对象三大特征:大量角色类的属性和行为存在重复代码,如果把一类的角色信息进行优化,提升代码复用,降低代码冗余。

面向对象学习的关键

  • 多关注语法的基本作业和流程
  • 多进行思考和语法记忆
  • 要自信,不要在短期想能做什么?

1、静态关键字:static

1.1、static 的作业、修饰成员变量的用法

  • static 是静态的意思,可以修饰成员变量和成员方法。
  • static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改

成员变量可以分为2类

  • 静态成员变量(通过static修饰):在class加载阶段就已经被声明,可以通过对象直接使用。

    //引用方法
    类名.静态成员变量
    对象.静态成员变量
    
  • 实例成员变量,每个对象实例都有自己的数据值。

    //引用方法
    对象.实例成员变量
    

1.2、静态成员变量的内存原理

public class User {
    //成员变量
    //静态实例变量,属于类,加载一次,可以被共享访问
    public  static  int onlineNumber = 160;

    //实例成员变量,专属于某一个实例对象
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void show(){
        System.out.println("onlineNumber为:"+onlineNumber+
                " "+this.name+"的年龄为:"+this.age);
    }

    public static void main(String[] args) {
        //类名.静态成员变量
        System.out.println(User.onlineNumber);
        System.out.println(onlineNumber);
        System.out.println(onlineNumber++);

        User user1 = new User();
        user1.name = "这是怎么回事儿?";  //private修饰的实例对象变量可以在本class中被其他方法调用
        user1.age = 20;
        user1.show();

        user1.onlineNumber++;  //极不推荐这样使用

        User user2 = new User();
        user2.name = "孙悟空";
        user2.age = 561;
        user2.show();
    }
}

内存原理图

Day10_面向对象进阶_第1张图片

注意:在加载class时,就已经为onlineNumber开辟了堆内存空间。

1.3、修饰成员方法的基本用法

​ 在过去的学习中,有的方法是有static定义的有的没有,两者的区别在于是否可以直接通过对象来调用。

成员方法的分类

  • 静态成员方法
    • 使用场景:如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。
    • 使用方法:
      • 类名.静态对象方法()
      • 对象.静态对象方法()
  • 实例成员方法
    • 使用场景:表示对象自己的行为,且方法中需要访问实例成员的。
    • 使用方法 : 对象.实例对象方法()

练习

public class Employee {
    public static String company = "bilibili";
    private String name;
    private  int age;
    private  String dept;

    //用于传输两个员工对象的年龄进入,并返回比较较大的年龄
    public static int compareByAge(int a,int b){
        return a>b?a:b;
    }
    //用于输出employee的具体内容
    public void showInfos(){
        System.out.println("名称:" + this.name +
                " 年龄:" + this.age + " 部门:"+ this.dept + " 公司:" + company);
    }

    public static void main(String[] args) {
        System.out.println(Employee.compareByAge(1,10));

        System.out.println(compareByAge(2,5));

        Employee employee = new Employee();
        employee.age =10;
        employee.name = "陈睿";
        employee.dept = "高层";
        employee.showInfos();
    }
}

内存存储方法示例

Day10_面向对象进阶_第2张图片

1.5、定义工具类

工具类

​ 工具类中定义的都是一些静态方法,每个方法都是以完成一个共用的功能为目的。

案例:在企业管理系统中,通常需要在一个系统的很多业务处使用验证码进行防刷新等安全控制。

现状分析:如果登录和注册等多处地方都存在验证码逻辑,就会导致同一个功能在多处开发,会出现代码重复度过高。

工具类的好处:调用方便,提高了代码复用

为什么工具类不适用实例方法做?

​ 实例方法需要创建对象调用,此时用对象知识为了调用方法,会浪费内存。

工具类相关注意对象

  • 建议将工具类的构造器进行私有,工具类无需创建对象。
  • 工具类内部都是静态方法,直接用类名访问。
//实例:在登录与注册过程中需要多次使用验证码的生成功能
public class VerifyTool {
    /**
       私有构造器
     */
    private VerifyTool(){
    }

    /**
      静态方法
     */
    public static String createCode(int n){
        // 1、使用String开发一个验证码
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        // 2、定义一个变量用于存储5位随机的字符作为验证码
        String code = "";
        // 3、循环
        Random r = new Random();
        for (int i = 0; i < n; i++) {
            int index = r.nextInt(chars.length());
            // 4、对应索引提取字符
            code += chars.charAt(index);
        }
        return code;
    }
}

//登录类
public class Login {
    public static void main(String[] args) {
        // 验证码:
        System.out.println("验证码:" + VerifyTool.createCode(10));
    }
}

//注册类
public class Register {
    public static void main(String[] args) {
        // 验证码:
        System.out.println("验证码:" + VerifyTool.createCode(5));
    }
}

练习

需求:在实际开发中,经常会遇到一些数组的使用工具类,编写一个ArrayUtils类实现一下功能

  • 请在ArraysUtils中提供一个工具类方法toString,用于返回整数数组的内容,返回的字符串如[10,20,30,40 ](只考虑一维数组)
  • 经常需要统计平均值,平均值为去掉最低分和最高分后的分值,请提供这样一个工具方法getAerage,用于返回 平均分。(只考虑浮点型数组,且只考虑一维数组
  • 定义一个测试类TestDemo,调用该工具类的工具方法,并返回结果。
public class ArrayUtils {
    //首先,私有化构造器
    private ArrayUtils(){}

    //静态声明toString方法
    public static String toString(int[] a){
        if (a == null) {
            return "[]";
        }
        String rs = "[";
        for (int i = 0; i < a.length; i++) {
            rs +=a[i]+",";
        }
        rs+="]";
        return  rs;
    }

    //静态声明getAerage方法
    public static double getAverage(int[] a){
        if (a == null) {
            return 0;
        }

        int sum = 0;
        for (double v : a) {
            sum+=v;
        }
        return 1.0*sum/a.length;
    }
}
public class Test2 {
    public static void main(String[] args) {
        int[] arr = {10, 20, 30};
        System.out.println(arr);
        System.out.println(ArrayUtils.toString(arr));
        System.out.println(ArrayUtils.getAverage(arr));

        int[] arr1 = null;
        System.out.println(ArrayUtils.toString(arr1));
        int[] arr2 = {};
        System.out.println(ArrayUtils.toString(arr2));
    }
}

1.6、static注意事项

  • 静态方法只能访问静态的成员变量,或者成员方法,因为静态方法和静态变量在class编译阶段就没声明并分配空间,而成员变量只有在对象实例化的时候才会被分配空间。
  • 实例方法可以访问静态的成员和实例成员。
  • 静态方法中不可以出现this。

2、Static应用知识:代码块

2.1、代码块的分类、使用

代码块概述

  • 代码块是类的五大成分之一(成员变量、构造器、方法、代码块、内部类),定义在类中方法外。
  • 在Java类下,使用{ }括起来的代码被称为代码块。

代码块分类

  • 静态代码块
    • 格式:static{ }
    • 特点:需要通过static关键字修饰,随着类的加载而加载,并且只自动触发、执行一次。
    • 使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。

2.1、应用实例

import java.util.ArrayList;

public class Card {
    /**
     模拟初始化牌操作
     点数: "3","4","5","6","7","8","9","10","J","Q","K","A","2"
     花色: "♠", "♥", "♣", "♦"
     1、准备一个容器,存储54张牌对象,这个容器建议使用静态的集合。静态的集合只加载一次。
     */
    public static ArrayList<String> cards = new ArrayList<>();

    /**
     2、在游戏启动之前需要准备好54张牌放进去,使用静态代码块进行初始化
     */
    static{
        // 3、加载54张牌进去。
        // 4、准备4种花色:类型确定,个数确定了
        String[] colors = {"♠", "♥", "♣", "♦"};
        // 5、定义点数
        String[] sizes = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
        // 6、先遍历点数、再组合花色
        for (int i = 0; i < sizes.length; i++) {
            // sizes[i]
            for (int j = 0; j < colors.length; j++) {
                cards.add(sizes[i] + colors[j]);
            }
        }
        // 7、添加大小王
        cards.add("小");
        cards.add("大");
    }

    public static void main(String[] args) {
        System.out.println(Card.cards);
    }
}

3、单例设计模式

3.1、设计模式、单例模式介绍、饿汉单例模式

什么是设计模式

  • 开发中经常遇到一些问题,一个问题通常有n中解法,但其中肯定有一种解法是最优的,这个最优解被人总结出来,称之为设计模式。
  • 设计模式有20多种,对应20多种软件开发中会遇到的问题,学设计模式主要是学2点:
    • 这种模式用来解决什么问题。
    • 遇到这种问题了,该模式是怎么写的,如何解决这个问题。

单例模式

  • 可以保证系统中,应用该模式的这个类永远只有一个实例,即该类永远只能创建一个对象。
  • 例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间。

饿汉单例模式

  • 定义一个类,把构造器私有。
  • 定义一个静态变量,存储一个对象。
//定义一个单例类
public class SingleInstance {
    //定义一个静态变量存储一个对象即可:属于类,与类一起加在一起
    public static SingleInstance singleInstance = new SingleInstance();
    //单例必须私有构造器
    private SingleInstance(){
        System.out.println("创建一个对象");
    }
    public static void main(String[] args) {
    }
}

3.2、懒汉单例模式

  • 只在真正需要该对象时,才回去创建一个对象(延迟加载对象)。

设计步骤:

  • 定义一个类,把构造器私有。
  • 定义一个静态变量存储一个对象。
  • 提供一个返回单例对象的方法。
public class SingleInstanceTest {
    //声明一个static实例对象
    public static SingleInstanceTest singleInstanceTest;

    //私有化构造器
    private SingleInstanceTest(){
        System.out.println("创建成功!!!");
    }

    //提供一个方法返回一个单例对象
    public static SingleInstanceTest getInstance(){
        /*如果还不存在实例化对象,创建一个对象
        如果已存在就直接返回已存在的实例
        * */
        if (singleInstanceTest == null) {
            singleInstanceTest = new SingleInstanceTest();
            return singleInstanceTest;
        }else{
            return singleInstanceTest;
        }
    }

    public static void main(String[] args) {
        System.out.println(SingleInstanceTest.getInstance());
        System.out.println("第二次:");
        System.out.println(SingleInstanceTest.getInstance());
    }
}

4、继承

4.1、继承概述、以及相关好处

什么是继承?

  • java提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起父子关系。
    • public class Student extends People{}
  • Student称为子类(派生类),People称为父类(基类 或超类)

使用继承的好处

  • 当子类继承父类之后,就可以直接使用父类公共的属性和方法了。因此,用好这个技术可以很好地提高代码复用性。
  • Java中子类更加强大
public class People {
    private String name;  //名字
    private int 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 static void main(String[] args) {
        Student student = new Student();
        student.setAge(11);
        student.setName("李天一");
        student.study();

        Teacher teacher = new Teacher();
        teacher.setAge(30);
        teacher.setName("李双江");
        teacher.teach();

    }
}
//继承People的学生类
class Student extends People{
    public void study(){
        System.out.println("努力学习");
    }
}

//继承People的类
class Teacher extends  People{
    public void teach(){
        System.out.println("教书育人");
    }
}

4.2、继承的设计规范、内存运行原理

继承设计规范

  • 子类们相同特征(共性属性,共性方法)放在父类中定义,子类独有的属性和行为应该定义在子类自己里面。

为什么?

  • 如果子类的独有属性、行为定义在父类中,会导致其它子类也会得到这些属性和行为,这不符合面向对象逻辑。

Day10_面向对象进阶_第3张图片

注意:在为子类分配空间的时候,会将数据在一个对象内分两部分存储,分类父类空间和子类空间

4.3、继承的特点

  • 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。
  • Java是单继承模式:一个类只能继承一个直接父类。
  • Java不支持多继承、但是支持多层继承。
  • Java中所有的类都是Object类的子类。

子类是否能够继承父类的构造器

​ 不能,子类有自己的构造器,父类构造器用于初始化父类对象。

子类是否可以继承父类的私有成员

​ 可以,但是不能直接访问。(即使是父类本身也不能直接调用自己的private成员)

Day10_面向对象进阶_第4张图片

子类是否可以继承父类的静态成员?

  • 不能,子类可以使用父类的静态成员,但是静态成员在class编译极端就被分配了空间,创建对象实例后都是直接将实例相关地址直接指向已分配的空间,导致静态变量可以被所有的继承子类所引用(同一个地址),所以不能算是继承,算是共享。

Java只支持单继承,不支持多继承。Java支持多层继承

Object特点:

  • java中所有类,要么直接继承了Object,要么间接继承了Object,Object是祖宗类。

4.4、继承后:成员变量、成员方法的访问特点

在子类方法中访问成员变量满足:就近原则

  • 先在子类中局部范围找
  • 然后子类成员范围找
  • 然后父类成员范围找

如果子父局部中出现了重名的成员,会优先使用局部,局部没有就使用子类中的,子类有没有才会使用父类中的,如果要优先使用特点变量怎么办

  • 可以通过this,指定子类成员
  • 通过super,指定父类成员。
public class ExtendTest02 {
    public static void main(String[] args) {
        Sheep sheep = new Sheep();
        sheep.show();
    }
}
class animal{
    public String type = "animal";
    public int age = 0;
}

class Sheep extends animal{
    public String type = "sheep";
    public int age = 0;

    public void show(){
        String type = "robbot";
        int age =10;
        System.out.println("父类成员:"+super.type +"  "+ super.age);
        System.out.println("子类成员:"+this.type +"  "+ this.age);
        System.out.println("局部成员:"+type +"  "+ age);
    }
}

4.5、方法重写

什么是方法重写

  • 在继承体系,子类出现了和父类中一模一样的方法声明,我们就称是子类这个方法是重写的方法。

方法重写的应用场景

  • 当子类需要父类的功能,但是父类的该功能不能满足自己的需求时。
  • 子类可以重写父类的方法。

案例演示

/*
旧手机的功能只能是基本的打电话,发信息
新手机的功能需要能够:基本的打电话下支持视频通话。基本的发信息下支持发送语音和图片。
*/
public class ExtendTest03 {
    public static void main(String[] args) {
        System.out.println("老手机:");
        OldPhone oldPhone = new OldPhone();
        oldPhone.call();
        oldPhone.sendInfo();

        System.out.println("\n新手机");
        NewPhone phone = new NewPhone();
        phone.call();
        phone.sendInfo();
    }
}
//老手机类
class OldPhone{
    public String number;
    public void call(){
        System.out.println("打电话!!!");
    }
    public void sendInfo(){
        System.out.println("发信息!!!");
    }
}
//新手机类
class NewPhone extends OldPhone{
    @Override
    public void call() {
        super.call();
        System.out.println("视频通话!!!");
    }

    @Override
    public void sendInfo() {
        super.sendInfo();
        System.out.println("发送语音和图片!!!");
    }
}

@Override重写注释

  • @Override是放在重写后的方法上,作为重写是否正确的校验注解
  • 加上该注解后如果重写错误,编译阶段会出现错误提示。
  • 建议所有的重写方法都加上@Override注解

方法重写注意事项和要求

  • 重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。
  • 私有方法不能重写
  • 静态方法不能重写
  • 子类重写父类方法时,访问权限必须大于或等于父类(暂时了解:缺省)

4.6、子类构造器的特点

子类构造器的特点

  • 默认先访问父类无参的构造器,再执行自己
  • 为什么?
    • 在子类初始化的时候可能会用到父类中的数据,如果父类没有完成初始化,无法完成构造。
    • 子类在初始化之前,要调用父类构造器完成父类数据空间的初始化
  • 如何调用父类构造器
    • 子类构造器默认第一句:super(),不写也存在。

4.7、子类构造器访问父类有参构造器

super调用父类有参构造器的作用

​ 初始化继承父类的数据。

如果父类中没有无参构造器,只有有参构造器会出现什么现象?

​ 会报错,子类默认执行父类无参构造器。

如何解决?

​ 子类构造器中可以通过书写super(…),手动调用父类有参构造器。

4.8、this和super使用总结。

  • this:代表本类对象的引用,super:代表父类存储空间的标识。
关键字 访问成员变量 访问成员方法 访问构造方法
this this.成员变量,访问本类成员变量 this.成员方法() this()
super super.成员变量,访问父类成员变量 super.成员方法() super()
class StudentTest {
    private  String school;
    private  String name;

    public StudentTest(String name){
        this.StudentTest(name,"蓝翔");
    }

    public void StudentTest(String name, String school) {
        this.name = name;
        this.school = school;
    }
}

this(…)和super(…)使用注意点:

  • 子类通过 this.(…)去调用本类的其他构造器,本类其他构造器会通过 super 去手动调用父类的构造器,最终还是会调用父类构造器的。
    成员变量 | 访问成员方法 | 访问构造方法 |
    | ------ | -------------------------------- | ---------------- | ------------ |
    | this | this.成员变量,访问本类成员变量 | this.成员方法() | this() |
    | super | super.成员变量,访问父类成员变量 | super.成员方法() | super() |
class StudentTest {
    private  String school;
    private  String name;

    public StudentTest(String name){
        this.StudentTest(name,"蓝翔");
    }

    public void StudentTest(String name, String school) {
        this.name = name;
        this.school = school;
    }
}

this(…)和super(…)使用注意点:

  • 子类通过 this.(…)去调用本类的其他构造器,本类其他构造器会通过 super 去手动调用父类的构造器,最终还是会调用父类构造器的。
  • 注意:this(…) ,super(…) 都只能放在构造器的第一行,所以二者不能共存在同一个构造器中。

你可能感兴趣的:(java学习,java,开发语言,后端)