JavaSE基础笔记——Javaoo(面向对象进阶:多态、内部类、常用API)

1、多态

多态的概述、优势

什么是多态?

多种形态

  • 同类型的对象,执行同一个行为,会表现出不同的行为特征。
  • 相同的行为,不同的实现。
  • 例子: 动物都有吃的行为, 兔子吃胡萝卜, 猫吃鱼, 狗吃肉 .

    相同的行为( 吃东西) 不同的实现( 胡萝卜,鱼,肉 )

多态的常见形式

父类类型 对象名称 = new 子类构造器;

接口 对象名称 = new 实现类构造器;

注意: 前提是两个类之间必须有 继承关系 和 实现关系

代码演示

测试类:

public class Test {
    public static void main(String[] args) {
        //1、多态的形式:父类类型 对象名称 = new 子类构造器;
        //               接口  对象名称 = new 实现类构造器;
        Animal a = new Dog();
        go(a);
        a.run();//方法调用:编译看左边,运行看右边
        System.out.println(a.name);//父类动物//变量调用,编译看左边,运行也看左边

        Animal a2 = new Tortoise();
        go(a2);
        a2.run();
        System.out.println(a2.name);//父类动物
    }

    /**
     要求:所有的动物都可以进来比赛
     */
    public static void go(Animal a){
        System.out.println("开始。。。");
        a.run();
        System.out.println("结束。。。");
    }
}

父类:

public abstract class Animal {
    public String name = "父类动物";
    public abstract void run();
}

子类:

public class Dog extends Animal{
    public String name = "子类狗";
    @Override
    public void run() {
        System.out.println("🐕跑的贼快---");
    }

    /**
     独有功能
     */
    public void lookDoor(){
        System.out.println("🐕在看🚪!!!");
    }
}

public class Tortoise extends Animal{
    public String name = "子类乌龟";
    @Override
    public void run() {
        System.out.println("🐢根本跑不了---");
    }

/**
     独有功能
     */
    public void layEggs(){
        System.out.println("🐢在下蛋---");
    }
}

多态中成员访问特点:

1、方法调用:编译看左边,运行看右边

2、变量调用:编译看左边,运行也看左边。(多态侧重行为多态

多态的前提:

有继承/实现关系;有父类引用指向子类对象;有方法重写。

多态的优势?

1、在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
2、定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。
 多态下会产生一个问题:多态下不能使用子类的独有功能。

代码演示(测试类):父类与子类同上面的。
 

public class Test {
    public static void main(String[] args) {
        Animal t = new Tortoise();
        go(t);
        Animal d = new Dog();
        go(d);

        //a2.lookDoor();//多态下不能访问子类独有功能
    }

    /**
     * 要求:所有的动物都可以进来比赛
     */
    public static void go(Animal a) {
        System.out.println("开始。。。");
        a.run();
        System.out.println("结束。。。");
    }
}

多态的类型转换问题 

1、自动类型转换(从子到父):又叫做向上转型

子类对象赋值给父类类型的变量指向。

2、强制类型转换(从父到子):又叫做向下转型

  • 此时必须进行强制类型转换:子类 对象变量 = (子类)父类类型的变量
  • 作用:可以解决多态下的劣势,可以实现调用子类独有的功能。
  • 注意:如果转型后的类型和对象真实类型不是同一类型,那么在转换时就会出现数据类型转换异常(ClassCastException).
  • 引用数据类型转换时 需注意:子类转为父类时 会造成数据的丢失

Animal t= new Tortoise();
Dog d = (Dog) t;//强制类型转换,编译阶段不报错的
// (注意:有继承或者实现关系编译阶段可以强转,没有毛病)运行时可能出错!
//数据类型转换异常(ClassCastException)

一dian小知识:

空指针异常

当某个对象为空对象时 ,并调用该对象的方法和属性时 就是会出现空指针异常

java.lang.NullPointerException 空指针异常

Java建议强制类型转换前使用instanceof关键字判断当前对象的真实类型,再进行强制转换。

作用: 就是用来判断关键字左边的变量指向的对象的真实类型,是否是右边的类型或者是其子类类型,是的话返回true,反之。

语法:

对象的变量名 instanceof 类名 表示该对象是一个什么类型的 返回一个布尔值(true 或者 false)

动态绑定: 代码在运行的时候才能确定被传入的实际类型. 比如: 传入父类的引用

动态参数: 代码在运行的时候才能确定被传入的实际参数 比如: 参数是父类的数据类型代码演示(测试类):父类与子类如上。

/**
 目标:学习多态形式下的类中转换机制
 */

public class Test {
    public static void main(String[] args) {
        //自动类型转换:
        Animal a = new Dog();
        a.run();

        //强制类型转换:
        Animal a2 = new Tortoise();
        a2.run();
        //Tortoise t = (Tortoise) a2;//从父类类型到子类类型,必须强制类型转换
        //格式:子类 对象变量 = (子类)父类类型的变量
        //t.layEggs();

        //Dog d = (Dog) a2;//强制类型转换,编译阶段不报错的
        // (注意:有继承或者实现关系编译阶段可以强转,没有毛病)运行时可能出错!
        //数据类型转换异常(ClassCastException)

        // java建议强转转换前使用instanceof判断当前对象的真实类型,再进行强制转换
        //变量名instanceof真实类型
        //判断关键字左边的变量指向的对象的真实类型,是否是右边的类型或者是其子类类型,是则返回true,反之
        if (a2 instanceof Tortoise){
            Tortoise t = (Tortoise) a2; //从父类类型到子类类型:必须强制类型转换
            t.layEggs();
        }else if (a2 instanceof Dog){
            Dog d = new Dog();
            d.lookDoor();
        }
        System.out.println("--------------------");
        go(new Dog());
        go(new Tortoise());
    }

    //实际开发中,在方法体里面,传递参数是用的Animal
    public static void go(Animal a){
        a.run();
        //a 它到底是乌龟还是狗,并不确定,所以必须加这段逻辑判断
        if (a instanceof Tortoise){
            Tortoise t = (Tortoise) a; //从父类类型到子类类型:必须强制类型转换
            t.layEggs();
        }else if (a instanceof Dog){
            Dog d = new Dog();
            d.lookDoor();
        }
    }
}

多态的综合案例

需求:

  • 使用面向对象编程模拟:设计一个电脑对象,可以安装2个USB设备。
  • 鼠标:被安装时可以完成接入,调用点击功能、拔出功能。
  • 键盘:被安装时可以完成接入,调用打字功能、拔出功能。

分析:

  • 定义一个USB接口(申明USB设备的规范必须是:可以接入和拔出)。
  • 定义两个USB实现类代表鼠标和键盘,让其实现USB接口,并分别定义独有的功能。
  • 创建电脑对象,创建2个USB实现类对象,分别安装到电脑里并触发功能的执行。

代码实现:

USB接口:

/**
 * USB接口 == 规范
 */

public interface USB {
    //接入  拔出
    void connect();
    void unconnect();
}

两个实现类(键盘与鼠标):

/**
 实现类:键盘
 */

public class KeyBoard implements USB{
    private String name ;

    public KeyBoard() {
    }

    public KeyBoard(String name) {
        this.name = name;
    }

    /**
     * 重写USB接入方法
     */
    @Override
    public void connect() {
        System.out.println(name + "成功链接了电脑--");
    }

    /**
     独有功能:按键
     */
    public void KeyDown(){
        System.out.println(name + "敲击了:来了老弟,666----没毛病----");
    }

    /**
     * 重写USB拔出方法
     */
    @Override
    public void unconnect() {
        System.out.println(name + "成功从电脑中拔出了--");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
/**
 实现类:鼠标
 */
public class Mouse implements USB {
    private String name;

    public Mouse() {
    }

    public Mouse(String name) {
        this.name = name;
    }

    /**
     * 重写USB接入方法
     */
    @Override
    public void connect() {
        System.out.println(name + "成功链接了电脑--");
    }

    /**
     * 独有功能:点击
     */
    public void dbClick() {
        System.out.println(name + "双击点亮小红心,一键三连----");
    }

    /**
     * 重写USB拔出方法
     */
    @Override
    public void unconnect() {
        System.out.println(name + "成功从电脑中拔出了--");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

电脑类:

public class Computer {
    private String name;

    public Computer() {
    }

    public Computer(String name) {
        this.name = name;
    }

    /**
     * 电脑开机的功能
     */
    public void start(){
        System.out.println(name + "开机了---");
    }

    /**
     提供安装USB设备的入口。
     */
    public void installUSB(USB usb){
        //多态:usb == 可能是鼠标,也可能是键盘
        //接入功能
        usb.connect();

        //独有功能:先判断,再强转。
        if (usb instanceof KeyBoard){
            KeyBoard k = (KeyBoard) usb;
            k.KeyDown();
        }else if (usb instanceof Mouse){
            Mouse m = (Mouse) usb;
            m.dbClick();
        }
        //拔出功能
        usb.unconnect();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

测试类:

/**
 目标:USB设备模拟
 1、定义USB接口:接入  拔出
 2、定义两个USB的实现类:鼠标、键盘。
 3、创建一个电脑对象,创建USB设备对象,安装启动
 */

public class Test {
    public static void main(String[] args) {
        //a、创建电脑对象
        Computer c = new Computer("外星人");
        c.start();//外星人开机了---

        //b、创建鼠标对象,键盘对象
        USB u = new Mouse("罗技");
        USB u1 = new KeyBoard("双飞燕");
        c.installUSB(u);//罗技成功链接了电脑--
                        //罗技双击点亮小红心,一键三连----
                        //罗技成功从电脑中拔出了--
        c.installUSB(u1);//双飞燕成功链接了电脑--
                        //双飞燕敲击了:来了老弟,666----没毛病----
                        //双飞燕成功从电脑中拔出了--
    }
}

多态总结

相同的行为,不同的实现

通过这句话,我们要掌握的内容:

1、多态是形容行为的,也就是”方法“,没有属性多态这么一说;

2、相同的行为指的是同名的方法;也就是说所有的多态方法至少要有一个特征,那就是方法名相同。

3、当然,我们在Java里特指的多态是两种:静态多态和动态多态。

首先明确这里的“静态”和static没有关系。这里的动和静是指程序在运行(运行期)还是没有运行(编译期)。

所谓”静态多态“指的是在编译期我们就能够确定程序运行的最终效果,即我们就能确定程序到底要执行的是哪个方法。

在语法上,重载是静态的,但是单独使用重写也是静态的。

”动态多态“是由两个技术共同组成的:重写和动态绑定技术。所以重写本身并不是动态的,动态效果是由动态绑定技术带来的。

这里需要强调一下“动态绑定技术”,它的核心归结于一个语法——父类的引用可以指向子类的对象。其实说白了,就是”=“两边的数据类型可以不一致。

我们先归纳一下基本数据类型的情况

double d = 3.14;//同类型数据值赋给同类型的变量
double d = 3;//小类型的数据值赋值给大类型的变量
int i = 3.14;//报错!!大类型的数据值赋给了小类型的变量
int i = (int) 3.14;//强转,有风险,风险是精度的丢失

总结一下:

本类型的值赋给本类型的变量,成功;

小类型的值赋给大类型的变量,成功;

大类型的值赋给小类型的变量,失败。解决手段是强制转换,风险是精度的丢失。

那么到了引用数据类型也是一样的

类型 A a = new 类型A();//成功
类型 A a = new 子类型B();//成功
子类型B b = new 类型A();//报错
子类型B b = (B)new 类型A();//强制,有风险,风险是编译通过了但是运行会报异常

为什么父类引用可以指向子类对象?

1、从场景上来说,父类的范围是大于子类的,既然这个变量是父类类型,子类和父类又是is-a关系,所以当然子类对象也是父类类型的一种了。

2、从内存上来说,子类对象的内部其实包含了一个完整的父类对象部分;这一点是Java继承的内部实现手段——”内存叠加“。

为什么子类引用不能指向父类对象?

1、场景上,父类范围大于子类,所以子类的变量是小类型的转不下大类型范围的数据;

2、从内存上,钱对象的内容中是没有子类人民币的特有属性和行为的,所以盒子变量会发现它找不到一个完整的人民币对象在内存中。

动态绑定技术的核心就是:”父类引用可以指向子类对象“;那么当我们声明一个父类变量的时候,它可以是A子类对象,也可以是B子类对象。只要运行起来以后,才能够确定这个父类引用到底指向了哪个子类对象!!绑定就是绑定这个!!

然后不同子类对象通过重写可能对同一个方法有着不同的实现,所以当它指向不同的子类对象的时候,调用同一个方法才有不同的效果。

不管你如何强转,如果要编译成功运行也成功,那么只有两种情况:本类指向本类,父类指向子类。

动态绑定的主要应用:

1、动态参数

public void method(父类 参数名){}

2、动态集合

父类[]集合 = new 父类[5];

集合[i] = 子类对象;

2、内部类

内部类是我们学到的可以在一个类里面书写的第五个内容。

内部类本身在Java当中也属于高阶用法。

首先建议第一个概念:内部类也是一个独立且完整的类,它可以有属性、构造、行为、初始化块、甚至还有内部类。所有的内部类在编译之后,都会有自己独立的class文件。只不过它的class文件的名字不在只是类名而已。

内部类概述:

  • 内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)。
  • 内部的作用: 它可以独立的继承某个类或者实现某个接口.

  • 不会受到外部类的继承和实现接口的影响.

  • 内部类可以非常优雅的解决java中单继承的问题

public class People{
    //内部类
    publiv class Heart{
    }
}

内部类的使用场景:

  • 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构可以选择使用内部类来设计。
  • 内部类通常可以方便访问外部类的成员,包括私有的成员。
  • 内部类提供了更好的封装性,内部类本身就可以用private protected等修饰,封装性可以做更多控制。

使用内部类如何解决多继承?

public class ChildBean extends  Father {

    public class InnerClass extends  Mother{
          
    }
}

内部类之一:静态内部类(了解)

  • 有static修饰,属于外部类本身的。
  • 使用场景:如果一个类包含了一个完整的成分。如:汽车类中的发动机类。
  • 它的特点和使用与普通类是完全一样的,类有的成分它都有,只是位置在别人里面而已。
public class Outer{
    //静态成员内部类
    public static class Inner{
    }
}

静态内部类创建对象的格式:

格式:外部类名.内部类名 对象名 = new 外部类名.内部类构造器();

范例:Outer.Inner in = new Outer.Inner();

注意(静态内部类的访问拓展):

  • 静态内部类中可以直接访问外部类的静态成员。
  • 不能直接访问外部类的实例成员。(外部类的实例成员必须用外部类对象访问)

代码演示:

/**
 外部类
 */

public class Outer {

    public static int a = 100;
    private String hobby = "打篮球。";

    /**
     学习静态成员内部类:有static修饰,属于外部类本身
     */
    public static class Inner{
        private String name;
        private int age;
        public static String schoolName ;
        public  void show(){
            System.out.println("名称:" + name);
            System.out.println(a);//静态内部类可以直接访问外部类的静态成员
            //System.out.println(hobby); //报错//不能直接访问外部类的实例成员。
            Outer o = new Outer();
            System.out.println(o.hobby);
        }

        public Inner() {
        }
        public Inner(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 static String getSchoolName() {
            return schoolName;
        }

        public static void setSchoolName(String schoolName) {
            Inner.schoolName = schoolName;
        }
    }
}
/**
 静态内部类创建对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类构造器
 静态内部类中可以直接访问外部类的静态成员。不能直接访问外部类的实例成员。
 */

public class Test {
    public static void main(String[] args) {
        Outer.Inner in = new Outer.Inner();
        in.setName("张三");
        in.show();  //名称:张三
                    //100
                    //打篮球。
    }
}

内部类之二:成员内部类(非静态内部类)(了解)

成员内部类 的创建需要依赖于外部类 

静态内部类 是不需要依赖外部类的

  • 无static修饰,属于外部类的对象。
  • JDK16之前,成员内部类中不能定义静态成员,JDK16开始也可以定义静态成员了。
public class Outer{
    //成员内部类
    public class Inner{
    }
}

静态内部类创建对象的格式:

格式:外部类名.内部类名 对象名 = new 外部类名.new 内部类构造器();

范例:Outer.Inner in = new Outer.new Inner();

注意(成员内部类的访问拓展):

  • 成员内部类可以直接访问外部类的静态成员。
  • 成员内部类的实例方法中可以直接访问外部类的实例成员。(因为必须先有外部类对象,才能有成员内部类对象,所以可以直接访问外部类对象的实例成员。)

代码演示:

/**
 外部类
 */

public class Outer {
    public static int num = 111;
    private String hobby;

    public Outer() {
    }

    public Outer(String hobby) {
        this.hobby = hobby;
    }

    /**
     成员内部类:不能加static修饰,属于外部类对象
     可以直接访问外部类的静态成员,实例方法中可以直接访问外部类的实例成员
     */
    public class Inner{
        private String name;
        private int age ;
        //public static int a ;//JDK16开始,成员内部类中开始支持静态成员了
//        public static void test(){
//            System.out.println(a);
//        }

        public Inner() {
        }

        public void show (){
            System.out.println("名称:" + name);
            //成员内部类可以直接访问外部类的静态成员。
            System.out.println("数量:" + num);
            //成员内部类的实例方法中可以直接访问外部类的实例成员。
            System.out.println("爱好:" + hobby);
        }

        public Inner(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;
        }
    }
}
/**
 成员内部类创建对象的格式:外部类名.内部类名  对象名 = new 外部类构造器 . new内部类构造器();
成员内部类可以直接访问外部类的静态成员。成员内部类的实例方法中可以直接访问外部类的实例成员。
 */

public class Test {
    public static void main(String[] args) {
        Outer.Inner in = new Outer().new Inner();
        in.setName("张三");
        in.show();
        //Outer.Inner.test();

        System.out.println("----------------");
        Outer.Inner in1 = new Outer("爱听歌").new Inner();
        in1.show();
    }
}

成员内部类面试题:

/**
 * 注意:在成员内部类中访问所在外部类对象,格式:外部类名.this
 */
/*
观察下面代码,写出合适的代码对应其注释要求输出的结果
 */
public class Test2{
    public static void main(String[] args) {
        People.Heart heart = new People().new Heart();
        heart.show();
    }
}

class People {
    private int heartbeat = 150;

    /**
     成员内部类的成员变量
     */
    public class Heart{
        private int heartbeat = 110;

        public void show(){
            int heartbeat = 78;
            System.out.println(heartbeat); //78
            System.out.println(this.heartbeat); //110
            System.out.println(People.this.heartbeat); //150
        }
    }
}

内部类之三:局部内部类(鸡肋语法,了解)

  • 局部内部类放在方法、代码块、构造器等执行体里。
  • 局部内部类的文件名:外部类$N内部类.class。

代码演示:

/**
 目标:了解局部内部类的语法
 */

public class Test {
    public static void main(String[] args) {
        class Cat{
            private String name;
            //public static int onLineNumber = 21;

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }
        Cat cat = new Cat();
        cat.setName("咖啡猫");
        }
    }
}

内部类之四:匿名内部类概述(重点)

  • 本质上是一个没有名字的局部内部类,定义在方法中、代码块中等等。
  • 作用:方便创建子类对象,最终目的为了简化代码编写。
  • 特点:

匿名内部类是一个没有名字的内部类;

匿名内部类写出来就会产生一个匿名内部类的对象;

匿名内部类的对象类型相对于是当前new的那个的类型的子类类型。

  • 格式:

new 类|抽象类名|或者接口名 (){
      重写方法;
};
  • 代码演示:

public class Test {
    public static void main(String[] args) {
/*        //2、使用多态去实例化老虎对象
        Animal a = new Tiger();
        a.run();*/
//但是匿名内部类就不这样,不需要定义子类
        Animal a = new Animal(){
            @Override
            public void run() {
                System.out.println("老虎跑得快---");
            }
        };
        a.run();
    }
}

/*//1、构建过程需要写一个子类
class Tiger extends Animal{
    @Override
    public void run() {
        System.out.println("老虎跑得快---");
    }
}*/

abstract class Animal{
    public abstract void run();
}

匿名内部类常见的使用形式

总结:匿名内部类可以作为方法的实际参数进行传输。

案例:某个学校需要让老师、学生、运动员一起参加游泳比赛。

分析:1、游泳设置为接口;2、使用匿名内部类创建好学生、老师、运动员重写接口的游泳方法;3、定义一个方法让所有角色来比赛

代码演示:

/**
 目标:掌握匿名内部类的使用形式
 */

public class Test2 {
    public static void main(String[] args) {
        Swimming s = new Swimming() {
            @Override
            public void swim() {
                System.out.println("学生快乐的自由泳🏊-");
            }
        };
        go(s);

        System.out.println("--------------------------");
        Swimming s1 = new Swimming() {
            @Override
            public void swim() {
                System.out.println("老师游🏊的贼快-----");
            }
        };
        go(s1);

        System.out.println("--------------------------");
        go(new Swimming() {
            @Override
            public void swim() {
                System.out.println("运动员游🏊的贼快啊-----");
            }
        });
    }

    /**
     学生  老师  运动员 可以一起参加游泳比赛
     */
    public static void go(Swimming s){
        System.out.println("开始---");
        s.swim();
        System.out.println("结束---");
    }
}

/*class Student implements Swimming{
    @Override
    public void swim() {
        System.out.println("学生快乐的自由泳🏊-");
    }
}*/


interface Swimming{
    void swim();
}

匿名内部类真实的使用场景演示

给按钮绑定点击事件:

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 目标:提高GUI编程 理解匿名内部类的真实使用场景
 */

public class Test3 {
    public static void main(String[] args) {
        //1、创建窗口
        JFrame win = new JFrame("登录界面");
        JPanel panel = new JPanel();//桌布
        win.add(panel);

        //2、创建一个按钮对象
        JButton btn = new JButton("登录");

        //注意:讲解匿名内部类的使用
/*        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(win,"点我一下,说明爱我!");//提示框
            }
        });*/

        btn.addActionListener(e -> JOptionPane.showMessageDialog(win,"别说话,吻我!!"));

        //3、把按钮对象添加到桌布上展示
        panel.add(btn);

        //4、展示窗口
        win.setSize(400,300);
        win.setLocationRelativeTo(null);//居中
        win.setVisible(true);
    }
}

使用总结:

开发里不是我们主动去定义匿名内部类的,而是别人需要我们写或者我们可以写的时候才会使用。

匿名内部类的代码可以实现代码进一步的简化(回扣主题)。

内部类面试题

1、有几篇Java文件是否就有几篇class文件? 否!

2、Java文件的名字是否和class文件的名字保持一致?否!

3、Java文件和class文件的关系?包括数量的关系和命名的关系

calss文件的数量永远跟类的数目保持一致,而不是Java文件;

class文件的命名也是跟类名有关系,其中内部类有相应的规则需要绑定外部类的类名;

4、内部类的分类:静态内部类、成员内部类、局部内部类、匿名内部类

5、局部内部类(包括匿名内部类),操作所属方法的局部变量的问题。

要把它当作常量操作。

6、成员内部类和静态内部类在第三方法类中new的语法。 (小概率)

成员内部类:"外部类名.内部类名 对象名 = new 外部类名.new 内部类构造器()"

静态内部类:“外部类名.内部类名 对象名 = new 外部类名.内部类构造器();”

3、常用API

API-Object、Objects

Object类的作用:

  • 一个类要么默认继承了Object类,要么间接继承了Object类,Object类是Java中的祖宗类。
  • Object类的方法是一切子类都可以直接使用的,所以我们要学习Object的方法。

Object类的使用方法

方法名说明
public String toString()

默认是返回当前对象在堆内存中的地址信息:类的权限名@内存地址

public Boolean equals(Object o)默认是比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false

1、Object的toString方法:

toString存在的意义:

父类toString()方法存在的意义就是为了被子类重写,以便返回对象的内容信息,而不是地址信息!!

代码演示:

学生类:

public class Student { //extends Object{
    private String name;
    private int age;
    private char sex;

    public Student() {
    }

    public Student(String name, int age, char sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    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 getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }




    /**
     * 重写toString方法
     * @return
     */
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        Student s = new Student("周雄",21,'男');

//         String rs = s.toString();
//         System.out.println(rs);

        //System.out.println(s.toString());

        //直接输出对象变量,默认可以省略toString()调用不写的
        System.out.println(s);//Student{name='周雄', age=21, sex=男}
    }
}

2、Object的equals方法:

equals存在的意义:

父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则。

代码演示:

学生类:

public class Student { //extends Object{
    private String name;
    private int age;
    private char sex;

    public Student() {
    }

    public Student(String name, int age, char sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    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 getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    /**
      自己重写equals,自己定制相等规则。
      两个对象的内容一样就认为是相等的。
     s.equals (s2)
     比较者:s1 == this
     被比较者:s2 ==》 o
     */
    /**
     * 重写equals方法
     * @param o
     * @return
     */
    @Override
    public boolean equals(Object o) {
        //1、判断是否是同一个对象比较,如果是返回true
        if (this == o) return true;
        //2、如果o是null返回false     如果o不是学生类型返回false。    Student!= o的类型
        if (o == null || this.getClass() != o.getClass()) return false;
        //3、说明o一定是学生类型,而且不为null
        Student student = (Student) o;
        return age == student.age &&
                sex == student.sex &&
                Objects.equals(name, student.name);
    }

    /**
    @Override
    public boolean equals(Object o){
        //1、判断o是不是学生类型
        if (o instanceof Student){
            Student s2 = (Student) o;//强转
            //2、判断两个学生的内容是否一样
//            if (this.name.equals(s2.name)
//                    && this.age == s2.age && this.sex == s2.sex){
//                return true;
//            }else{
//                return false;
//            }
            return this.name.equals(s2.name) &&
                    this.age == s2.age && this.sex == s2.sex;
        }else {
            //学生只能和学生比较,否则结果一定是false
            return false;
        }
    }
     */

}

测试类:

public class Test2 {
    public static void main(String[] args) {
        Student s1 = new Student("张三",19,'男');
        Student s2 = new Student("张三",19,'男');
        //equals 默认是比较2个对象的地址是否相同。
        System.out.println(s1.equals(s2));//true
        System.out.println(s1 == s2);//false
    }
}

Objects概述

  • Objects类与Object还是继承关系,Objects类是从Jdk1.7开始之后才有的。

Objects类的常见方法

方法名说明

public static boolean equals(Object a , Object b)

比较两个对象的,底层会先进行非空判断,从而可以避免空指针异常,再进行equals比较

public static boolean isNull(Object obj)

判断变量是否为null,为null返回true,反之

官方在进行字符串比较时,没有用对象自己的equals方法,而是选择了Objects的equals方法来比较两个对象。

Objects的equals方法比较的结果是一样的,但是更安全。:因为对象名称有可能是null,如果用null.equals的话会报错(空指针异常),但是用Objects.equals,内部会先进行非空校验。

代码演示:

public class Test {
    public static void main(String[] args) {
        String s1 = null;

        String s2 = new String("itheima");
        //System.out.println(s1.equals(s2)); //留下了隐患,可能出现空指针异常

        System.out.println(Objects.equals(s1, s2)); //false//更安全 结果更准确

/**
 源码分析:
 public static boolean equals(Object a, Object b) {
 判断是不是同一对象     判断a是不是为null
 return (a == b) || (a != null && a.equals(b));
 }
 */

        System.out.println(Objects.isNull(s1));//true
        System.out.println(s1 == null); //true
        System.out.println(Objects.isNull(s2));//false
        System.out.println(s2 == null); //false
    }
}

对象进行内容比较的时候建议使用Objects提供的equals方法。

API-StringBuilder概述

  • StringBuilder是一个可变的字符串类,我们可以把它看成是一个对象容器。
  • 作用:提高字符串的操作效率,如拼接、修改等。

StringBuilder构造器:

名称

说明

public StringBuilder()创建一个空白的可变的字符串对象,不包含任何内容
public StringBuilder(String str)创建一个指定字符串内容的可变字符串对象

StringBuilder常用方法:

方法名称说明
public StringBuilder append(任意类型)

添加数据并返回StringBuilder

public StringBuilder reverse()将对象内容反转
public int length()返回对象内容长度
public String toString()提高toString()就可以实现把StringBuilder转换为String

代码演示:

/**
 目标:学号使用StringBuilder操作字符串,最终还需要知道它性能好的原因。
 StringBuilder是一个可变的字符串类,我们可以把它看成一个对象容器。
 作用:提高字符串的操作效率,如拼接、修改等
 常用方法:1、append:添加数据并返回StringBuider对象本身;2、reverse:将对象内容反转
 3、length:返回对象内容长度;4、tostring :实现StringBuilder转换为String
 */

public class StringBuilderDemo1 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder(); // 先建立一个空的
        sb.append("a");
        sb.append("b");
        sb.append("c");
        sb.append(1);
        sb.append(false);
        sb.append(3.3);
        sb.append("abc");
        System.out.println(sb);//abc1false3.3abc

        StringBuilder sb1 = new StringBuilder();
        //支持链式编程。
        sb1.append("a").append("b").append("c").append("我爱你中国");
        System.out.println(sb1);//abc我爱你中国

        //反转
        sb1.reverse().append("110");
        System.out.println(sb1);//国中你爱我cba110

        //长度
        System.out.println(sb1.length());//11

        //注意:StringBuilder只是拼接字符串的手段,效率好。
        //最终的结果还是要恢复成String类型。
        StringBuilder sb2 = new StringBuilder();
        sb2.append("123").append("456");
        //恢复成String
        String rs = sb2.toString();
        check(rs);//123456
    }

    /**
     * 写一个传入字符串的方法(检查字符串内容对不对)
     * @param data
     */
    public static void check(String data){
        System.out.println(data);
    }
}

为什么拼接、反转字符串建议使用StringBuilder?

String:内容是不可变的、拼接字符串性能差。

StringBuilder:内容是可变的、拼接字符串性能好,代码优雅。

案例:打印整型数组内容

需求:设计一个方法,用于输出任意整型数组的内容,要求输出成如下格式:

“该数组内容为:[11,22,33,44,55]”

public class StringBuiderTest2 {
    public static void main(String[] args) {
        int[] arr1 = null ;
        System.out.println(toString(arr1));

        int[] arr2 = {10,88,99} ;
        System.out.println(toString(arr2));

    }

    /**
     1、定义方法,接收任意整型数组,返回数组内容格式
     */
    public static String toString(int[] arr){
        if (arr != null){
            //2、开始拼接内容
            StringBuilder sb = new StringBuilder("[");
            for (int i = 0; i < arr.length; i++) {
                sb.append(arr[i]).append(i == arr.length - 1 ? "": ",");
            }
            sb.append("]");
            return sb.toString();
        }else{
            return null;
        }
    }
}

API-Math

Math类:

  • 包含执行基本数字运算的方法,Math类没有提供公开的构造器。
  • 如何使用类中的成员?看类的成员是否都是静态的,如果是,通过类名就可以直接调用

Math类的常用方法:

方法名说明
public static int abs(int a)

获取参数绝对值

public static double ceil(double a)向上取整
public static double floor(double a)向下取整
public static int round(float a )四舍五入
public static int max(int a ,int b )获取两个int值中的较大值
public static double pow (double a , double b)返回a的b次幂的值
public static double random()返回值为double的随机数,范围[0.0 , 1.0]
/**
 Math类:包含执行基本数字运算的方法,Math类没有提供公开的构造器。
 如何使用类中的成员:看类的成员是否都是静态的,如果是,通过类名就可以直接调用
 常用方法如下:abs:取绝对值;ceil:向上取整;floor:向下取整;round:四舍五入
 pow:求指数次方;random:随机数(范围0 - 1.0);max:获取两个int值中的较大值
 */

public class MathDemo {
    public static void main(String[] args) {
        //1、取绝对值:返回正数。
        System.out.println(Math.abs(10)); //10
        System.out.println(Math.abs(-10.3)); //10.3

        //2、向上取整:5
        System.out.println(Math.ceil(4.00000001));//5.0

        //3、向下取整:4
        System.out.println(Math.floor(4.999999999));//4.0

        //4、求指数次方
        System.out.println(Math.pow(2,3)); // 2 ^ 3 = 8.0

        //5、四舍五入 10
        System.out.println(Math.round(4.49999)); // 4
        System.out.println(Math.round(4.500001)); // 5

        //6、随机数
        System.out.println(Math.random()); //0.0 - 1.0(包前不包后)

        //扩展:3-9之间的随机数 (0-6) + 3
        //(0 - 1) * 7 + 3
        int data = (int)(Math.random() * 7) + 3 ;
        System.out.println(data);

        //7、获取两个int值中的较大值
        System.out.println(Math.max(5,10));//10
    }
}

API-System 

System类概述

  • System的功能是通用的,都是直接用类名调用即可,所以System不能实例化。

总结System

System.in——标准输入流,指的是控制台输入
System.out——标准输出流
System.err——标准错误输出流

System.exit(0);——关闭JVM,其中0是关闭代码,0代表的是正常
System.arrayCopy();——数组的拷贝
System.gc();——召唤垃圾回收,进行一次垃圾收集,但是收不收,收谁,什么时候收,不受这句代码的控制。
System.currentTimeMills;——获取系统的当前时间毫秒值

System类的常用方法

方法名说明

public static void exit(int status)

终止当前运行的java虚拟机,非零表示异常终止

public static long currentTimeMillis()

返回当前系统的时间毫秒值形式

public static void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数)

数组拷贝

时间毫秒值:

  • 计算机认为时间是有起点的,起始时间为:1970年1月日 00:00:00
  • 时间毫秒值:指的是从1970-1-1 00: 00: 00 走到此刻的总的毫秒值。1s = 1000ms。
  • 时间毫秒值用处:可以进行时间的计算:性能分析。

代码演示:

/**
 System类概述
 常用方法:1、public static void exit(int status) :终止当前运行的java虚拟机,非零表示异常终止
 2、public static long currentTimeMillis() :返回当前系统的时间毫秒值形式
 3、public static void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数):数组拷贝
 */

public class SystemDemo {
    public static void main(String[] args) {
        System.out.println("程序开始。。。");

//        System.exit(0); // JVM终止!

        // 1s = 1000ms

        // 2、计算机认为时间有起源:返回1970-1-1 00: 00: 00 走到此刻的总的毫秒值:时间毫秒值。
/*        long time = System.currentTimeMillis();
        System.out.println(time);

        long startTime = System.currentTimeMillis();
        //进行时间的计算:性能分析
        for (int i = 0; i < 100000; i++) {
            System.out.println("输出: " + i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println((endTime - startTime)/1000.0 + "s");*/

        //3、使数组拷贝(了解)
        /**
         arraycopy(Object src , int srcPos,
         Object dest , int destPos,
         int length)
         参数一:被拷贝的数组
         参数二:从哪个索引位置开始拷贝
         参数三:复制的目标数组
         参数四:粘贴位置
         参数五:拷贝元素的个数
         */
        int[] arr1 = {10,20,30,40,50,60,70};
        int[] arr2 = new int[6]; // [0,0,0,0,0,0] ==> [0,0,40,50,60,0]
        System.arraycopy(arr1 , 3 , arr2 , 2 , 3);
        System.out.println(Arrays.toString(arr2));
        
        System.out.println("程序结束。。。。");
    }
}

API-BigDecimal (大数据类型)

BigDecimal作用:

  • 用于解决浮点型运算精度失真的问题。

使用步骤:

  • 创建对象BigDecimal封装浮点型数据(最好的方式是调用valueof方法)

public static BigDecimal valueOf (double val):包装浮点数成为BigDecimal对象。

BigDecimal a1 = BigDecimal.valueOf(a);

BigDecima常用API

方法名说明

public BigDecimal add (BigDecimal b ) 

加法

public BigDecimal subtract (BigDecimal b )

减法

public BigDecimal multiply(BigDecimal b )

乘法

public BigDecimal divide (BigDecimal b )

除法

public BigDecimal divide(另一个BigDecimal对象,精确几位,舍入模式 ) 

除法

代码演示:

/**
 BigDecimal作用:用于解决浮点型运算精度失真的问题
 创建对象BigDecimal封装浮点型数据(最好方法是调用方法)
 public static BigDecimal valueOf (double val):包装浮点数成为BigDecimal对象。
 常用api:1、public BigDecimal add (BigDecimal b ) :加法
 2、public BigDecimal subtract (BigDecimal b ) :减法
 3、public BigDecimal multiply(BigDecimal b ) :乘法
 4、public BigDecimal divide (BigDecimal b ) :除法
 5、public BigDecimal divide(另一个BigDecimal对象,精确几位,舍入模式 ) :除法
 */

public class BigDecimalDemo {
    public static void main(String[] args) {
        //浮点型运算的时候直接+  *  /  可能会出现数据失真(精度问题)。
        System.out.println(0.09 + 0.01);//0.09999999999999999
        System.out.println(1.0 - 0.32);//0.6799999999999999
        System.out.println(1.015 * 100);//101.49999999999999
        System.out.println(1.301 / 100);//0.013009999999999999

        System.out.println("-----------------");
        double a = 0.1;
        double b = 0.2;
        double c = a + b;
        System.out.println(c);//0.30000000000000004
        System.out.println("-----------------");

        //包装浮点型数据成为大数据对象BigDecimal
        BigDecimal a1 = BigDecimal.valueOf(a);//0.1
        BigDecimal b1 = BigDecimal.valueOf(b);//0.2
        //BigDecimal c1 = a1.add(b1);//0.3       //加
        //BigDecimal c1 = a1.subtract(b1);//-0.1 //减
        //BigDecimal c1 = a1.multiply(b1); //0.02//乘
        BigDecimal c1 = a1.divide(b1);//0.5     //除
        System.out.println(c1);

        //目的:double
        //还需要把BigDecimal的对象转换成double
        double rs = c1.doubleValue();
        System.out.println(rs);

        //注意:BigDecimal是一定要精度运算的
        BigDecimal a11 = BigDecimal.valueOf(10.0);
        BigDecimal b11 = BigDecimal.valueOf(3.0);
/*        BigDecimal c10 = a11.divide(b11);
        System.out.println(c10);*/ //会报错崩掉
        /**
         参数一:除数  参数二:保留小数位数  参数三:舍入模式:HALF_UP四舍五入
         */
        BigDecimal c11 = a11.divide(b11 , 2 , RoundingMode.HALF_UP);
        System.out.println(c11);//3.33
        
        System.out.println("------------------");
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值