Java基础五

一、数组

1、数组创建语法

数据类型[] 变量名 = new 数据类型[长度];

2、用数组创建的对象与用类创建的对象的区别

对象的创建方式对象中的元素个数元素的数据类型元素的访问方式是否支持使用循环来遍历访问元素元素是否有零值对象存储的位置
使用类创建由成员变量的个数决定可以在类中自定义对象名.成员变量名否 (无索引号)堆区
使用数组创建由数组的长度决定所有元素是同一种类型数组名[索引号]是 (有索引号, 从0开始)堆区

3、基本数据类型和引用数据类型

  • 基本数据类型的数组有8个
  1. byte[]
  2. short[]
  3. int[]
  4. long[]
  5. float[]
  6. double[]
  7. boolean[]
  8. char[]
  • 引用数据类型的数组(使用类创建的数组)
    使用类可以造对象,一个对象可以存储一行数据
    使用类可以造数组,一个数组可以存储多个对象,称为对象数组,可以存储一个二维表结构的数据

一个普通Java对象 存储一行数据
一个基本类型数组 存储一列数据
一个引用类型数组 存储一张二维表

package com.tongda.chapter04;

public class Demo01 {
    public static void main(String[] args) {
        //创建多个对象繁琐
        Student s1 = new Student();
        Student s2 = new Student();
        Student s3 = new Student();
        Student s4 = new Student();
        Student s5 = new Student(); //对象的构造方法是() 可以有参/无参

        //用数组就简单很多,一次性创建
        //优点:方法栈只需要一个数组变量就可以操作多行数据
        Student[] sArray = new Student[10]; //数组的构造方法是[],必须有一个长度参数
        System.out.println(sArray);
        //遍历数组
        for (int i = 0; i < sArray.length; i++) {
            //默认值为null
            System.out.println(sArray[i]);//与sArray[0]地址不同
            // 因为sArray是一个对象地址,sArray[0]是sArray下标为0所在的对象地址
        }
        //造对象并存入到数组中
        // sArray = new Student(); //左:Student[]类型 右:Student类型
        // 不能直接把学生对象赋给sArray变量
        sArray[0] = new Student();
        sArray[1] = new Student();
        sArray[2] = new Student();
        sArray[3] = new Student();
        sArray[4] = new Student();
        System.out.println(sArray);
        for (int i = 0; i < sArray.length; i++) {
            System.out.println(sArray[i]); //使用数组名[索引号]访问数组中的元素
        }
    }
}

在这里插入图片描述

4、数组使用常见错误

错误一:访问未初始化/未开辟内存空间的数组
public class Demo02 {
    public static void main(String[] args) {
        int[] array;
        // 数组未初始化之前不能访问数组中的元素
        array[0] = 10;
    }
}
错误二: 初始化数组未指定长度
public class Demo02 {
    public static void main(String[] args) {
        //int[] array = new int[]; // 错误: 初始化数组的时候必须指定数组的长度
        // 初始化数组, 并分配长度为10的内存空间
        int[] array = new int[10]; // 初始化数组的时候必须指定数组的长度
    }
}
错误三: 数组下标越界
public class Demo02 {
    public static void main(String[] args) {
        // 初始化数组, 并分配长度为10的内存空间
        int[] array = new int[10]; // 初始化数组的时候必须指定数组的长度
        // array[-1] = 10;
        // array[10] = 10;
    }
}

5、创建数组的四种方式

public class Demo02 {
    public static void main(String[] args) {
        // 方式1:1. 在栈区声明数组变量 2. 在堆区开辟数组内存空间 (两行代码)
        int[] array;
        array = new int[10];
        // 方式2:1. 在栈区声明数组变量 2. 在堆区开辟数组内存空间 (一行代码)
        int[] array2 = new int[10];
        // 方式3:1. 在栈区声明数组变量 2. 在堆区开辟数组内存空间 3. 在数组对象中的每个索引中存入数据
        int[] array3 = new int[]{10,20,30,40,50};
        // 方式4是方式3的简化版:1. 在栈区声明数组变量 2. 在堆区开辟数组内存空间 3. 在数组对象中的每个索引中存入数据
        int[] array4 = {10,20,30,40,50};
    }
}

6、遍历数组的三种方式

public class Demo03 {
    public static void main(String[] args) {
        int[] array = {10,20,30,40,50};
        //正序遍历 array.fori
        System.out.println("正序遍历");
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]); //此处i所指的是下标
        }
        //倒序遍历 array.forr
        System.out.println("倒序遍历");
        for (int i = array.length - 1; i >= 0; i--) {
            System.out.println(array[i]); //此处i所指的是下标
        }
        //不使用索引号遍历,增强型for循环 array.for
        System.out.println("不使用索引号遍历");
        //优点:语法最easy 缺点:循环中没有索引号
        for (int e : array){
            System.out.println(e); //此处e所指的是元素
        }
    }
}

7、二维数组

public static void main(String[] args) {
        //二维数组:了解即可,更多使用的是对象数组,对象数组本质上就是特殊的二维数组
        String[][] array = new String[5][];
        //为二维数组中第一个元素开辟一个一维数组
        array[0] = new String[5];
        array[1] = new String[5];
        array[2] = new String[5];
        array[3] = new String[5];
        array[4] = new String[5];

        //第一个[]表示在二维数组中找到某个一维数组
        //第二个[]表示在找到的一维数组中找某个索引号中的元素
        array[0][1] = "";
    }

二、面向对象的三大特征

1、封装

  • 使用private修饰符,修饰类的成员变量,让外部不可以直接对类的成员变量进行读写操作,防止写入非法数据到对象中
  • 针对每一个私有属性提供一组public修饰符修饰的setter()写方法和getter()读方法给外部调用
  • 在setter()方法和getter方法中可以编写读写之前的数据校验、逻辑判断、格式转换等代码
  • setter()方法有参数,无返回值
  • getter()方法有返回值,但是没有参数
public class Demo05 {
    public static void main(String[] args) {
        Person person = new Person();
        //存入对象中的数据是一些不合法的数据,怎么避免?
        //直接给成员遍历赋值这个过程无法检查赋的值是否合法

        //person.name = null;
        //person.age = -18;
        //使用private修饰了name和age,这两个成员变量就被Person类隐藏了
        //name和age不再直接暴露给外部,只在Person类内部可见

        //person.name = "张三";//报错

        //为什么不直接赋值,而构造一个成员方法?如此麻烦!
        //通过参数传入想要存入在name成员变量中的数据
        //好处:可以在赋值之前进行检查,过滤非法数据/校验数据
        person.setName("张三");
        person.setAge(-1);
        //私有后对外部不可见,必须提供对私有属性的读写方法给外部调用
        //私有的目的,不是让外部可以访问,而是不可以直接访问,防止外部赋予非法数据
        //System.out.println(person.name);
        //System.out.println(person.age);
        //编写get方法来对private数据进行读写
        person.getName();
        person.getAge();
    }
}
public class Person {
    private String name;
    private int age;

    public void setName(String name){
        this.name = name;
    }
    public void setAge(int age){
        if(age < 18){
            age = 18;
        }
        this.age = age;
    }
    public String getName(){
        return this.name;
    }
    public int getAge(){
        return this.age;
    }
}

在这里插入图片描述

2、继承

I 四种访问权限修饰符

访问权限本类本包的类子类非子类的外包类注释
public本包/非本包/子类访问
protected本包/子类访问
default本包访问
private本类访问

在这里插入图片描述

II 继承

  • 继承发生在两个类之间,是类与类之间的一种关系,被继承的类为父类,继承的类为子类,关键字为extends
  • 造子类对象的时候,会同时造出一个父类对象,并且是先造父类对象,再造子类对象,因为类的构造方法中的第一行处,有一句隐式的 super(); 代表调用其父类的无参构造方法
  • 造出来的父类对象存储在子类中的super变量中,子类可以通过super这个变量访问父类对象中的成员
  • 子类可以继承到父类的所有非private修饰的成员变量和成员方法
  • 子类不可以继承父类的构造方法,但可以通过super();来调用父类的构造方法
  • 如果一个类没有继承任何类,那么将默认继承Object类,Object类是官方提供了一个所有类的顶层父类
  • Java不支持多继承,但支持多级继承,即一个子类只能有一个直接父类,但可以有多个间接父类
public class Father extends Person{
    String name = "大王";
    protected String name1 = "大王1";
    private String name2 = "大王2";
    static String name3 = "大王3";
    public String name4 = "大王4";

    public Father(){
        System.out.println("执行Father的构造方法");
    }

    public void drive(){
        System.out.println("开车去诗和远方~");
    }
}
/*
* Son extends Father Son继承了Father
* Son - 子类 Father - 父类
* */
public class Son extends Father{
    String name = "小王";

    public Son(){
        //super();隐性suoer()方法
        System.out.println("执行Son的构造方法");
    }

    public void show(){
        System.out.println("Son name:" + this.name);
        System.out.println("Father name:" + super.name);
        System.out.println("Father name1:" + super.name1);//非private都可以访问
//        System.out.println("Father name2:" + super.name2);//private不能访问
        System.out.println("Father name3:" + super.name3);
        System.out.println("Father name4:" + super.name4);
    }

    public static void main(String[] args) {
        //调用Son()构造方法,造了一个Son对象
        Son son = new Son();
        son.show();
        son.drive();
    }
}

III 查看类图关系

在这里插入图片描述
在这里插入图片描述

IV 加入类

官方的类,搜索加入
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
自己写的类,直接拖过来会自动匹配关系
在这里插入图片描述

V 构造方法追溯

son()—>Father()—>Person()—>Object
在这里插入图片描述
在这里插入图片描述

3、多态

I 源代码文件的四种形式类

① class
普通类
抽象类

父类与子类的关系:一对多
在这里插入图片描述
将共有属性抽象出来,让子类去继承父类
在这里插入图片描述
继承的好处:

  • 相同成员变量和成员方法可以定义在父类中,让所有子类继承,减少冗余代码
  • 父类中可以定义抽象方法,让子类重写,这样可以实现运行时多态,减少冗余代码,提高程序的可扩展性

② 接口 interface
③ 枚举 enum
④ 注解 annotation

II 多态

  • 编译器多态
    通过方法重载实现, 发生在同一个类中, 方法名相同, 但是参数列表不同
  • 运行时多态
    通过方法重写实现, 必须要有继承关系, 然后子类重写父类的方法, 在编译期使用父类作为数据类型, 即不确定具体的类型, 在运行时, 由传入的具体对象来确定具体的数据

III 重载和重写

重载 Overload 和 重写 Override
相同点: 体现的都是方法的多态, 即一个方法可以实现多种功能
eg:Player 类的 feed 方法可以 实现 喂养各种不同类型的宠物
区别:

重载
  1. 需要在Player类中定义多个feed方法, 参数是不同的具体类型
  2. 这是一种编译时的多态
    优点: 简单直接, 好理解 (每新增一种宠物, 只需要添加一个feed()方法即可)
    缺点: 扩展性差, 代码复用性不足
//feed()方法是编译时多态的,feed()方法发生了方法重载
/**
 * 喂食猫
 */
public void feed(Cat cat){ // 在编译期已经确定这个参数必须是Cat类型
}
/**
 * 喂食狗
 */
public void feed(Dog dog){ // 在编译期已经确定这个参数必须是Dog类型    
}
/**
 * 喂食企鹅
 */
public void feed(Penguin pgn){ // 在编译期已经确定这个参数必须是Penguin类型
}
重写
  1. 必须要有继承
  2. 只需要在Player类中定义一个feed方法, 然后参数是抽象的父类类型
  3. 这是一种运行时多态
    优点: 扩展性极强, 代码最大程度复用 (每新增一种宠物, 只需要让它继承父类, 然后重写父类的抽象方法即可)
    确点: 不易理解
//feed()方法是运行时多态的,eat()方法发生了方法重写
/**
 * 喂食宠物
 */
public void feed(Pet pet){ 
// 在编译期并不确定pet是什么类型, 而是在运行时, feed()方法被调用时, 传入的具体对象来确定pet的类型
}
/*
* Pet - 宠物
* 猫、狗、企鹅...公共的特征(属性/成员变量)和行为(方法)
* 1、如果在各自类中编写他们的属性和方法,必然有大量冗余/重复的代码
* 2、从代码设计角度,可以考虑将这些冗余代码抽象出来,形成一个父类
* */
public abstract class Pet {
    String nickName;
    int health;
    int love;
    int level;

    /**
     * 1、abstract修饰的方法为抽象方法
     * 2、为什么不直接把子类中的eat方法直接挪到父类中来?
     *      因为多个子类eat方法的逻辑并不是完全一样,无法直接挪到父类让子类继承
     * 3、eat方法为什么必须要在父类中定义?
     *      若不定义,则pet.eat()报错,因为找不到eat()方法
     * 4、eat方法为什么被定义为抽象方法?
     *      父类中定义eat()方法是为了pet.eat()语法正确
     *      父类的eat方法并不需要写任务代码,因此定义为抽象方法
     * @param food
     * @return
     */
     //---设置eat方法为抽象方法为抽象方法,无需写任何代码
    public abstract boolean eat(String food);
}
public class Cat extends Pet{
    String color;

    /**
     * 食物只能是"鱼" + 30、"高级猫粮" + 20、"普通猫粮" + 10
     * @param food 食物的名称
     * @return 成功或失败
     */
    public boolean eat(String food){
        if(this.health == 100){
            System.out.println(this.nickName + ":健康值已经满了,不需要喂食!");
            return false;
        }
        switch (food){
            case "普通猫粮":
                this.health += 10;
                break;
            case "高级猫粮":
                this.health += 20;
                break;
            case "鱼":
                this.health += 30;
                break;
            default:
                System.out.println(this.nickName + ":食物种类错误!");
                return false;
        }
        this.health = this.health > 100 ? 100 : this.health;
        return true;
    }
}
public class Dog extends Pet{
    String strain;

    public boolean eat(String food){
        if(this.health == 100){
            System.out.println(this.nickName + ":健康值已经满了,不需要喂食!");
            return false;
        }
        switch (food){
            case "普通狗粮":
                this.health += 8;
                break;
            case "高级狗粮":
                this.health += 16;
                break;
            case "骨头":
                this.health += 25;
                break;
            default:
                System.out.println(this.nickName + ":食物种类错误!");
                return false;
        }
        this.health = this.health > 100 ? 100 : this.health;
        return true;
    }
}
package com.tongda.chapter04;

public class Player {
    String name;
    double money;

    /*
    * 喂食宠物
    * */
    public void feed(Pet pet){
        pet.eat("");
    }
    /*
    *  喂食宠物
    *  */
//    public void feed(Cat cat){
//        boolean b = cat.eat("鱼");
//        if(b){
//            System.out.println("喂食成功,金币-50");
//            this.money -= 50;
//        }
//    }
//    public void feed(Dog dog){
//        boolean b = dog.eat("骨头");
//        if(b){
//            System.out.println("喂食成功,金币-50");
//            this.money -= 50;
//        }
//    }
}
package com.tongda.chapter04;

public class Demo06 {
    public static void main(String[] args) {
        //宠物猫对象
        Cat cat = new Cat();
        cat.nickName = "喵喵";
        cat.health = 50;
        cat.love = 50;
        cat.level = 1;

        //宠物狗对象
        Dog dog = new Dog();
        dog.nickName = "汪汪";
        dog.strain = "拉布拉多";
        dog.health = 50;
        dog.love = 50;
        dog.level = 1;

        //宠物企鹅对象
        Penguin penguin = new Penguin();
        penguin.nickName = "QQ";
        penguin.sex = "雌性";
        penguin.health = 50;
        penguin.love = 50;
        penguin.level = 1;

        //玩家对象
        Player player = new Player();
        player.name = "小明";
        player.money = 1000;

        //玩家喂养宠物
        //Cat cat = new Cat(); 正确
        player.feed(cat);
        //Cat cat = new Dog(); 错误
        //player.feed(dog);
        player.feed(dog);
        //如果引用是父类类型,那么可以指定不同类型的子类对象
        //Pet p1 = new Cat(); //继承Pet
        //Pet p2 = new Dog(); //继承Pet
        //Pet p3 = new Penguin(); //继承Pet
        //Pet p4 = new Pig(); //未继承Pet

        //player.feed(penguin);
        //Pet.eat();//Pet中并没有这个方法
    }
}

三、作业

完成使用重载和重写两种方式实现一个feed方法喂养多种宠物, 并思考区别

1、重写

特点:不易理解,但代码扩展性高,可复用度高
Eg: 每增加一种宠物,只需让其继承父类即可
父类
package com.tongda.homework20220225;

//定义抽象类
public abstract class Pet {
    String nickName;
    int health;
    int love;
    int level = 0;


    //定义抽象方法
    public abstract boolean eat(String food);

    public void setHealth(int health){
        if (health <= 100) this.health = health;
    }

    public void setLove(int love){
        if (health <= 100) this.love = love;
    }

}

子类

package com.tongda.homework20220225;

//子类继承父类
public class Cat extends Pet{
    String color;

    //定义eat()方法
    public boolean eat(String food){
        if(this.health == 100){
            System.out.println(this.nickName + ":当前健康值已经满了,不需要喂食了!");
            return false;
        }
        switch (food){
            case "普通猫粮":
                this.health += 15;
                break;
            case "高级猫粮":
                this.health += 25;
                break;
            case "鱼":
                this.health += 35;
                break;
            default:
                System.out.println(this.nickName + ":我不喜欢吃这个!");
                return false;
        }
        if(this.health > 100){
            this.health = 100;
            level++;
        }
        System.out.println("太好了,喂食成功!");
        System.out.println(this.nickName + "当前健康值为:" + this.health);
	System.out.println(this.nickName + "当前等级为:" + this.level);
        return true;
    }
}

子类

package com.tongda.homework20220225;

//子类继承父类
public class Dog extends Pet{
    String strain;

    //定义eat()方法
    public boolean eat(String food){
        if(this.health == 100){
            System.out.println(this.nickName + ":健康值已经满了,不需要喂食!");
            return false;
        }
        switch (food){
            case "普通狗粮":
                this.health += 40;
                break;
            case "高级狗粮":
                this.health += 50;
                break;
            case "骨头":
                this.health += 60;
                break;
            default:
                System.out.println(this.nickName + ":我不喜欢吃这个!");
                return false;
        }
        if(this.health > 100){
            this.health = 100;
            level++;
        }
        System.out.println("太好了,喂食成功!");
        System.out.println(this.nickName + "当前健康值为:" + this.health);
	System.out.println(this.nickName + "当前等级为:" + this.level);
        return true;
    }
}

子类

package com.tongda.homework20220225;

//子类继承父类
public class Penguin extends Pet{
    String sex;

    //定义eat()方法
    public boolean eat(String food){
        if(this.health == 100){
            System.out.println(this.nickName + ":健康值已经满了,不需要喂食!");
            return false;
        }
        switch (food){
            case "乌贼":
                this.health += 20;
                break;
            case "磷虾":
                this.health += 30;
                break;
            case "沙丁鱼":
                this.health += 40;
                break;
            default:
                System.out.println(this.nickName + ":我不喜欢吃这个!");
                return false;
        }
        if(this.health > 100){
            this.health = 100;
            level++;
        }
        System.out.println("太好了,喂食成功!");
        System.out.println(this.nickName + "当前健康值为:" + this.health);
	System.out.println(this.nickName + "当前等级为:" + this.level);
        return true;
    }
}

玩家类

package com.tongda.homework20220225;

public class Player {
    String name;
    double money;

    //定义feed方法,参数为父类抽象类型
    public void feed(Pet pet, String food){
        System.out.println("---------------------------");
        if(money <= 50) System.out.println("对不起,您的余额不足50元,请充值!");
        else {
            if(pet.eat(food)){
                this.money -= 50;
                System.out.println("您当前余额为:" + this.money);
            }//根据参数调用不同对象的eat()方法
        }
    }
}

游戏类

package com.tongda.homework20220225;

import java.util.Scanner;

public class Game {
    public static void main(String[] args) {

        //养只猫
        Cat cat = new Cat();
        cat.nickName = "helloKitty";
        cat.setHealth(50);
        cat.setLove(50);
        cat.level = 1;
        cat.color = "白色";

        //养只狗
        Dog dog = new Dog();
        dog.nickName = "小七";
        dog.setHealth(50);
        dog.setLove(50);
        dog.level = 1;
        dog.strain = "神犬";

        //养只企鹅
        Penguin penguin = new Penguin();
        penguin.nickName = "QQ";
        penguin.setHealth(50);
        penguin.setLove(50);
        penguin.level = 1;
        penguin.sex = "雌";

        //一个玩家
        Player player = new Player();
        player.name = "ABU";
        player.money = 1000;

        //喂宠物
        player.feed(cat,"鱼");
        player.feed(dog,"骨头");
        player.feed(penguin,"巧克力");

    }
}

2、重载

特点:易理解,但代码扩展性及复用度低,可能会造成代码冗余
Eg: 每增加一种宠物,添加对应参数类型的feed方法即可

父类

package com.tongda.homework20220225;

public class Pet {
    String nickName;
    int health;
    int love;
    int level = 0;

    public void setHealth(int health){
        if (health <= 100) this.health = health;
    }
    public void setLove(int love){
        if (health <= 100) this.love = love;
    }
}

玩家类

package com.tongda.homework20220225;

public class Player {
    String name;
    double money;

    public void feed(Cat cat, String food){
        System.out.println("---------------------------");
        if(money <= 50) System.out.println("对不起,您的余额不足50元,请充值!");
        else {
            if(cat.eat(food)){
                this.money -= 50;
                System.out.println("您当前余额为:" + this.money);
            }        }
    }
    public void feed(Dog dog, String food){
        System.out.println("---------------------------");
        if(money <= 50) System.out.println("对不起,您的余额不足50元,请充值!");
        else {
            if(dog.eat(food)){
                this.money -= 50;
                System.out.println("您当前余额为:" + this.money);
            }
        }
    }
    public void feed(Penguin penguin, String food){
        System.out.println("---------------------------");
        if(money <= 50) System.out.println("对不起,您的余额不足50元,请充值!");
        else {
            if(penguin.eat(food)){
                this.money -= 50;
                System.out.println("您当前余额为:" + this.money);
            }
        }
    }
}

其他不变

3、测试结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白*进阶ing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值