Java学习笔记 06、面向对象—三大特性(二)

简介: Java学习笔记 06、面向对象—三大特性(二)

四、Object超类


Object:Java中所有类的始祖,也就是祖宗(根父类),每个类都是由它扩展而来的,包括数组的父类也是Object。如果在类中声明未使用extends关键字指明父类,就默认父类为java.lang.Object类。


Objcet类结构预览图:所有类都具有了其Object的方法,Object无属性,其并不是抽象类,可以进行实例化,只有一个空参构造器,其中部分方法是final类型,子类无法重写。



问1:Object不是抽象类,其是具体的,为什么会允许有人去创建Object对象?是不是不合理?


因为有时候就会会需要一个通用的对象,一个轻量化的对象。最常见的用途是用来线程的同步化上。

问2:Object主要目的是提供多态的参数与返回类型吗?


①作为多态让方法可以应付多种类型的机制。

②提供Java在执行期对任何对象都有需要的方法。

③有一部分与线程相关。

问3:既然多态这么有用,为什么不把所有参数与返回类型都设置为Object?


java对于保护程序代码有一项重要机制"类型安全检查",使用多态时再编译期间只会将左边声明类作为实例,这也代表你只能调用该类的方法。例如:Object o = new Person(); o.walk(); 那么第二条调用方法无法通过编译,因为walk()在Object中并没有,编译期间只会认定Object作为实例。

Java是类型检查很强的语言,编译器会检查你调用的是否是该对象确实可以响应的方法。


1、==运算符与equals方法

==运算符


==运算符:可使用于基本数据类型变量与引用数据类型变量


基本数据类型变量:比较两个保存的字面值是否相等(不一定类型相同,如整型、浮点型)

引用数据类型变量:比较两个对象的地址值是否相等。实际上就是两个引用地址是否是在堆中同一个开辟的对象(即在堆中的地址)。

比较特殊举例:


//基本数据类型:整型与浮点型比较
int a = 1;
int b = 1.0;
System.out.println(a == b);//true
//引用类型:创建对象的String字符串   堆中开辟的内存地址不同,所以为false
String str = new String("changlu");
String str1 = new String("changlu");
System.out.println(str == str1);//fasle
//引用类型:直接赋值的String字符串  直接赋值的是存于方法区中的常量池里,相同的字符串会引用同样的地址,不会再重新开辟一份空间
String str2 = "changlu";
String str3 = "changlu";
System.out.println(str2 == str3);//true


equals方法


equals:仅适用于引用数据类型


每个类都会有默认的equals方法(因为都继承了Object类),看一下Object类中equals方法:


public boolean equals(Object obj) {
    return (this == obj);
}


在Object中的equals方法与==的作用相同,比较两个引用地址值是否相同。


而实际上我们对于对象的比较应该是对其中类的属性进行比较,所以一般都会重写equals方法,在String,Date,File,包装类中实际上重写了Object类中的equals方法,这些实际上都是比较其实体内容是否相等。


看一下String类中重写的equals方法:


public boolean equals(Object anObject) {
  //首先看是否是一个地址的引用
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}


2、toString()方法


toString():用来描述我们对象的信息


首先看一下Object中的toString()方法:


public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}


其中的内存对象地址是虚拟地址,在操作系统之上盖了一层JVM,JVM相当于虚拟的操作系统,所以说自定义类(没重写equals方法的)输出的是虚拟的地址,而不是真实的内存地址。


我们之前使用System.out.println()输出一个自定义对象时,输出的是一个如Boy@677327b6,实际上就与这个Object的toString()有关,看一下源码:


//System源码:System.out =》PrintStream out = null;
public void println(Object x) {
    //调用了String的valueOf()
    String s = String.valueOf(x);
    synchronized (this) {
        print(s);
        newLine();
    }
}
//String源码:valueOf()方法
public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}


我们可以看到通过println()输出的实际上就是自定义对象toString()方法,valueOf()方法中采用了多态,由于我们自定义类没有重写toString()方法,所以会默认调用Object中的进行输出也就是一个虚拟地址值了。


toString()自定义如下:


class Person{
    public String name = "person";
    //这是IDEA自动生成的toString,我们也可以修改成自己想要输出的形式
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Main {
    public static void main(String[] args){
        System.out.println(new Person());//Person{name='person'}
    }
}


通过自定义toString()方法,我们再次输出就不再是一个虚拟地址值了(并不是说明栈中引用变量不再是堆中的内存地址了),可以输出类中自定义内容。



五、包装类


介绍拆装箱与转换问题

包装类:针对于八种基本类型定义相应的引用类型,也称为封装类。


基本数据类型

包装类

byte

Byte

boolean

Boolean

short

Short

char

Character

int

Integer

long

Long

float

Float

double

Double


JDK5.0之后引入了装箱与拆箱的概念。


装箱操作:Integer i = 15; 隐藏了Integer.valueOf(15)方法,实际上也是通过new 对象获得的对象实例。

拆箱操作:int i1 = i; 隐藏了Integer.intValue()方法,返回int值。


针对于包装类如何创建实例?可直接赋值或者通过有参构造器。


一般直接赋值,如Integer i = 1;,或者通过有参构造器 Integer i = new Integer(100);

比较特殊的是Boolean类提一下,其通过构造器创建实例如Boolean boolean = new Boolean("tRue");,其中true与false可以不区分大小写,其他有误。


基本数据类型与包装类转换:


包装类 =》基本数据类型:xxxValue()

基本数据类型 =》包装类:valueOf(xxx)

基本数据类型、包装类与String类转换:


基本、包装 =》String类:String.valueOf(xxx)

String类 =》基本数据类型:对应包装类.parsexxx()


Integer包装类容易混淆情况

在jdk1.5之后,Integer与int之间能够进行自动拆箱与装箱。


Integer中的缓存数组问题


先看这个例子:


@Test
public void test02(){
    Integer i = 10;
    Integer j = 10;
    System.out.println(i == j);//true
}


两个Integer包装类实例直接赋值10,中间会有装箱的过程会自动调用Integer.valueOf()方法,此时就会有一个疑问抛出,为什么我们声明的是两个引用类型,最后==判断引用地址问什么相同?

针对于该问题我们应当去Integer源码中一探究竟:


//包装类源码
public final class Integer extends Number implements Comparable<Integer> {
    //装箱过程会调用valueOf()方法,其中会对i进行判断是否在[-128,127]中,若是的话直接返回一个cache[xx]指定下标值,
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            //若是10,则返回cache[138]=>10
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    //静态内部类IntegerCache:用于预先存储[-128,127]的所有值到Integer数组cache中,数组中都是一个个Integer实例
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
        static {
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            //cache[0]赋值-128向上累加
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
            assert IntegerCache.high >= 127;
        }
        private IntegerCache() {}
    }
}


总结:在进行装箱过程中使用的valueOf(int i)方法,其中i若是在[-128-127]区间中,那么返回就是就是cache数组中已经保存好的Integer实例(初始化类时保存),所以上面的程序结果为true,都是在cache数组中同一个实例。



new Integer()案例


@Test
public void test02(){
    Integer i = new Integer(10);
    Integer j = new Integer(10);
    System.out.println(i == j);//false
}


注意:这里是new的实例,在堆中是各自独立开辟出来的对象实例,所以结果为false,并没有调用valueOf()这个方法。


看下源码:


public Integer(int value) {
    this.value = value;
}


在Integer包装类中使用final int value来存储数据的。


相关面试题

1.三元运算符:若是包含三元运算符,会进行自动类型提升,例如下面的Integer会提升为Double,最后输出1.0。


Object o = true?new Integer(1):new Double(2.0);
System.out.println(o);//1.0


2.if-else判断对于自动类型不会提升。


Object o;
if(true){
    o = new Integer(12);
}else{
    o = new Double(12.0);
}
 System.out.println(o);//12


参考文章

[1]. Java创建子类实例的时候也会创建父类实例吗?


[2]. 子类将继承父类所有的属性和方法吗?为什么? 看其回答


[3]. [java]为什么System.in/out/err值为null?


[4]. 书籍《head first java 2.0》

相关文章
|
2月前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
78 2
|
10天前
|
存储 Java 开发者
什么是java的Compact Strings特性,什么情况下使用
Java 9引入了紧凑字符串特性,优化了字符串的内存使用。它通过将字符串从UTF-16字符数组改为字节数组存储,根据内容选择更节省内存的编码方式,通常能节省10%至15%的内存。
|
20天前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
35 6
|
16天前
|
Java
java中面向过程和面向对象区别?
java中面向过程和面向对象区别?
19 1
|
29天前
|
JavaScript 前端开发 Java
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。如果你从我的文章中受益,欢迎关注我,我将持续更新更多优质内容。你的支持是我前进的动力!🎉🎉🎉
24 0
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
|
1月前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
33 4
|
1月前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
37 2
|
2月前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
95 3
|
2月前
|
存储 安全 Java
Java Map新玩法:深入探讨HashMap和TreeMap的高级特性
【10月更文挑战第19天】Java Map新玩法:深入探讨HashMap和TreeMap的高级特性,包括初始容量与加载因子的优化、高效的遍历方法、线程安全性处理以及TreeMap的自然排序、自定义排序、范围查询等功能,助你提升代码性能与灵活性。
29 2
|
1月前
|
Java 数据库连接 API
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
45 0
下一篇
DataWorks