java进阶第二讲-数组、String类

本文深入探讨了Java中数组的概念、类型、定义及操作方法,并详细解析了String类的特性,包括不可变性和字符串常量池的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java进阶第二讲-数组、String类

1 回顾一下Object

Object中的方法:
	public native int hashCode();
	带有native关键字的方法调用的是底层C++的dll文件
    这种方法我们在工具中看不到具体的实现,当然也可以去其他地方找相关的实现代码。
    hashCode()它是一个对象在堆中的内存地址经过hash算法计算之后又进行了进制的转换得出的结果.返回值是int
    问题:如果两个对象的hashCode()值一样,说明什么问题?是同一个对象
    hashCode()方法有什么用?
        因为hashCode()方法计算的对象是内存地址,内存地址对于一个对象来讲是惟一的,所以hashCode()算法算出来的也是唯一值。因此这个方法可以判断:
        1. 一个对象是否存在。
        2. 一个对象与另一个对象是否是同一个对象。
        3. 两个对象的hashCode值比较的话,就相当于引用之间使用"==",只能比较地址,不能比较值。
        
      protected void finalize() throws Throwable { }
Called by the garbage collector on an object 
when garbage collection determines that there are no more references to the object.
    // 当GC发现没有任何的引用指向当前对象的时候,这个方法就会被GC自动调用
    // 这个方法被调用的时候,对象被回收了吗?还没有,也就是说,对象在被GC回收之前,
    // GC会自动调用这个finalize()方法
    这个方法其实就是SUN公司java工程师为程序员准备的一个对象销毁之前的时刻。可以直观的观测对象销毁之前的状态。
public class AarrayTest01 {

    @Override
    protected void finalize() throws Throwable {
        System.out.println("对象即将被GC回收!");
    }

    public static void main(String[] args) throws Throwable {
        Object o = new Object();
        // 翻看源码,回到上一次停留的地方ctr + alt + ←
        System.out.println(o.hashCode());

        Object o1 = new Object();
        // 翻看源码,回到上一次停留的地方ctr + alt + ←
        System.out.println(o1.hashCode());
        AarrayTest01 a = new AarrayTest01();
        for (int i = 0; i < 100000; i++) {
            System.out.println(new AarrayTest01());
        }
    }
}

2 数组

  • 声明:declare 在java中只能声明一个变量,也就是说没有给这个变量赋初始值。
  • 定义:define 在java中,定义一个变量,就是给变量赋值的过程
int[] a = {1,2}; // 推荐这种方式,这种格式更有利于理解数组
int a1[];// 这种方式也行,API中大部分是这种格式
public class AarrayTest01 {

    public static void main(String[] args) throws Throwable {
        // 数组
        int[] a = {1,2}; // 推荐这种方式,这种格式更有利于理解数组
        int a1[];// 这种方式也行,API中大部分是这种格式

        System.out.println(a);
        // 什么结果?内存中的首地址,什么形式呢?
        // [I@16d3586----类型名 + @ + 内存地址hash后的十六进制表示
        // [I@16d3586 格式其实是Object对象的toString()执行之后才会得到的结果
        // System.out.println(a);它自动调用了Objcet的toString()方法
        // 为什么数组会自动调用Object类的toString方法?说明数组也是引用类型的。
        // 它的父类是Object。只有这种解释。
        // 什么是引用类型? 类型 变量名,这个变量名指向的是一块内存空间,我们说它就是引用类型。
        // A a = 内存地址; 这种形式就是引用类型。java当中,只有两种类型,基础数据类型和引用类型
        // 引用类型所指向的内存地址实际是对象(常量、static修饰的变量)的内存地址。
        // 这也说明了,数组是引用类型。我们也可以推断,数组的本身是保存在堆中的。
    }
}

  • Object类的简介
/**
 * Class {@code Object} is the root of the class hierarchy.
 * Every class has {@code Object} as a superclass. All objects,
 * including arrays, implement the methods of this class.
 */
  • 数组是一种数据类型,但是它不是基础数据类型,是引用数据类型。
  • 基础数据类型:byte short int long float double boolean char
  • 除去这8中之外,全是引用类型。
public class AarrayTest01 {

    public static void main(String[] args) throws Throwable {
        // 数组
        int[] a = {1,2}; // 推荐这种方式,这种格式更有利于理解数组
        int a1[];// 这种方式也行,API中大部分是这种格式

        System.out.println(a.getClass().getName());
        // a.getClass()后为什么可以直接"."getName():因为getClass()返回了引用(一个对象的引用)
        // 为什么返回的是引用,而不是对象?
        // 因为对象在堆中,很大,引用只是一个地址,很小,从效率上来讲,你说是传引用还是传对象?
        // 我们一般讲传输,都是要通过网络、通过电脑中的bus,
        // 传一个几个Byte数据好,还是传几百M上G的数据好呢?---传引用更好
        // 好比,你在银行有1个亿的存款,你去取钱,是拿一张有一个亿的卡好呢,还是拿1亿的现金?
        // 你要拿1亿现金,家里先得修一个装得下1亿现金的金库。如果你拿一张卡,就是拿了一个引用
        // 这个引用指向的是银行的金库,自己没有必要再修一个金库。
        // 在java中,基础数据类型都是传值
        // 引用数据都是传引用(这里的传:实参和形参之间的传递方式,方法的返回值返回的方式)
        // java做好了封装,引用类型都是传递的都是引用。返回值类型也都是引用,没有返回过值。
        // 在C中C++中,可以传值,也就是说可以传指针所指向的内存地址的值。但这样不好。
        // 你想,你传一个值出来,其实是做了一份拷贝,那么你就一定要有一块同等大小的空间去接。
        // 浪费空间。由于值可能很大,传输上耗时,效率低下。
        // 谁能"."?"类名."和"引用.","类名."静态的属性或者是方法;"引用."成员属性或者是方法
        // 一个方法的返回值类型有哪些?void 基础数据类型 引用数据类型
        // 基础数据类型可以"."方法吗?不能的。void返回的是null,null也是一个引用类型
        // 它不能"."任何东西,一旦"."就会报NullPointerException
        // 能"."成员方法的只有引用类型。
    }
}

  • 通过实验,我们看到了数组拥有Object类中所有的方法。所以我们也能断定它是Object的子类,数组也是引用类型。数组的引用如果被置空,也会报空指针异常。
  • 数组有一个属性:length
    • length属性,指的是数组的长度。
    • 任何一个数组都有这个属性。

2.1 数组的类型

  • 基础数据类型的数组:int[] a;这意味着,a所指向的数组中保存的是int类型的数据
  • 引用类型的数组:Person[] p;这意味着,p所指向的数组中保存的是Person类型的对象的引用。

2.2 数组的定义

  • 静态初始化
    • int[] a = {1,2,3,4};
  • 动态初始化
    • int[] a = new int[大小];
    • 然后再给数组中的元素赋值。

2.3 访问数组中元素的方式

  • 取下标的方式进行访问,下标从0开始到length-1
  • 注意:在java中,“=” 后面的大括号中的内容就是一个对象,在javascript中也是这样。
  • 这种用法,目前来说只对数组成立。数组赋值的时候,int[] a = {……};这是静态的初始化方式

2.4 数组的内存图

在这里插入图片描述

2.5 两种初始化方式的差别

  • 静态初始化是在已知数组元素值的时候,可以直接静态初始化。
  • 静态初始的时候,实际上是根据{}中的值的个数来分配了数组的内存空间
  • 动态初始化的时候,实际上是根据new int[初始化大小]中的初始化大小的值来分配内存空间
  • 动态初始化是指,后续可以给数组中的元素进行赋值。
  • 如果是静态初始化,一开始初始化的时候在{}中没有给值,如果在后续赋值,会抛出Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 0。数组索引越界异常。为什么?因为静态初始化的时候,JVM会根据{}中的值的个数在堆中给数组分配大小。
  • 这也说明一个问题,一旦数组的大小被确定之后,是不能改变的。
int[] arr = {};
arr[0] = 1;
//Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
int[] arr1 = new int[3];
arr1[3] = 100;//ArrayIndexOutOfBoundsException: 3
  • 数组一旦被创建,其大小就固定了,无论静态的还是动态的。大小是固定的。数组的大小是不可以被改变的。也就是说length值已经被确立了。我们可以想象,这个length属性一定被final修饰。

2.6 数组的优缺点

  • 优点

    • 数组在堆中是一块连续的内存空间
    • 数组中存放的是同一类型的值
    • 我们要访问某个元素,是通过数组的下标进行访问,因为连续,元素的类型相同,因为每个元素的大小都是固定的,我们又能拿到首元素的地址,所以访问的速度会很快,效率很高
  • 缺点:

    • 数组中的元素存放在一段连续的内存空间上,而且数组一旦被创建,大小就固定了,不能改变。对于超出当前数组大小范围的数据元素的存放,就需要另外开辟一个更大的数组空间,进行数组的拷贝。这样增加系统的开销。
    • 数组的删除和增加,都会造成大量的数据移动,效率不高。

2.7 数组的扩容

  • 数组的扩容,本质上没有在原有的数组所在的堆内存空间上进行扩展,而是另外重新开辟了一块内存空间。也就是说,原来的int[] a = 0x1234;扩容以后的int[] a 不再指向0x1234,而是指向了新开辟的数组的内存地址(0x2345)
  • 数组的扩容的步骤:
    第一步:开辟一个更大的数组
    第二步:将原数组中的内容拷贝过来
    第三步:将要存入的元素追加到更大数组中相应的位置
    第四步:将原数组的引用指向新数组的对象

2.8 为什么推荐使用 “类型[] a”

  • 因为:类型[] 表示是一个数组类型,后边的变量指的是这个数组的引用。
  • 如果是 “类型 标识符[]”这种形式,会让人误解。也很难体现出引用数据类型的特征。
  • 所以,我们推荐使用 类型[] 数组名 的方式

2.9 二维数组

  • 二维数组的形式:
int[][] arr
  • 二维数组的定义
int[][] arr = {
    {1,2,3}, 
    {4,5,6}, 
    {7}
}; ----静态的方式
    
int[][] arr = new int[3][5]; ----动态的方式

3 String类

public final class String //意味着:这是一个最终的类,没有子类,不能被继承
// All string literals in Java programs, 
// such as "abc", are implemented as instances of this class. 
// Strings are constant; their values cannot be changed after they are created
这句话什么意思?
    字符串是常量,它们的值在它们被创建之后不能改变。
    String str = "abc";
	is equivalent to:等价于
	char data[] = {'a', 'b', 'c'};
	String str = new String(data);

分析:常量,什么是常量?存放在哪里?
    final static修饰的是常量
    存放在方法区内存中
    
    String的本质是什么?也就是说String是怎么实现的?
    char[] 数组。
    我们可以想到,char[]数组中的元素要不能被改变,则一定要被final修饰。
    
    String类型的数据全部存放于方法区的"字符串常量池"中。字符串一旦被创建,不能改变。
    String name = "abc";// 这意味着String是引用类型,name是引用类型的变量名
// 引用类型的变量只能保存地址,不能保存实际的值。
// "abc"不是内存地址,它是一个实实在在的值。
// 所以"abc"赋值给name,实际上是将"abc"存放的内存地址的值给到了name。
// 并不是将"abc"的本身给到了name。那么,"abc"这个字符串一定是一个对象
// 这个对象存放在方法区内存中的字符串常量池中。
// 为什么要怎么做?要将字符串存放到方法区内存中的字符串常量池中?
// 为什么要规划这么一块内存空间?
// 对事物的描述,人类最能接受的方式是字符串。字符串是不是最利于识别的,也是用得最多的
// 使用率太高了,如果把它放在堆中,是不是很占空间。也很消耗JVM的性能。
// 把字符串做成常量的话,利用率是不是高一些。重复在堆中创建销毁的次数是不是得到了缓解。

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

// 测试代码
public class AarrayTest01 {

    public static void main(String[] args) throws Throwable {
        String str = "abc";
        str = "def";
        // 这是什么意思?是两个字符串对象被创建了,def的地址给到了str这个引用类型变量
        // 常量池中仍然存在"abc"
        // 字符串常量一旦建立就不能被改变

        System.out.println("abc".hashCode());
        System.out.println(str.hashCode());

        str += "d";
        // 这里是合并了吗?覆盖了原来的abc吗?没有,创建了一个新的

        System.out.println(str);
        String str1 = "abc";
        String string = "abc";
        System.out.println(str1 == string);
        System.out.println(string.hashCode());
        System.out.println("------------------");

        // 请问这里创建了几个字符串对象?3 存在于字符串常量池中的有几个?3
        // "abc" "d" "abcd"
        // str = str + "d" ----> "abcd"
    }
}

    

ng = “abc”;
System.out.println(str1 == string);
System.out.println(string.hashCode());
System.out.println("------------------");

    // 请问这里创建了几个字符串对象?3 存在于字符串常量池中的有几个?3
    // "abc" "d" "abcd"
    // str = str + "d" ----> "abcd"
}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值