3.待整理

为什么字符串是常量,在创建后不能被修改

因为String是被final修饰的,它的属性value是一个final修饰的char数组。

final修饰的。这就意味着value在构造器中被初始化后,无法再被修改了。

String是线程安全的吗?

String是不可变类,所以是线程安全的

☆String类被final修饰,不代表String对象是被final修饰

String的值在创建之后不能更改

String的值是不可以改变的,注意是值。因为value是一个final修饰的char数组,final修饰的。

这就意味着value在构造器中被初始化后,无法再被修改了。

事实上String对象内容的改变是通过内存地址的改变来完成的。

public class TestString {
    public static void main(String[] args) {
        String a = "hello";
        System.out.println(a);
        
        //相当于 new String("hello world") 此时引用a指向的内存地址发生了变化
        //并不是值发生了变化
        //a指向了1个新的对象
         a+= " world"; 
        System.out.println(a);
        
        // 注意String类是被final修饰 不代表String对象是被final修饰的
        // final String age = "1";
        // age = age + "2"; 报错
    }
}

在这里插入图片描述

Hello的内容并没有发生变化。只是a的指向发生了变化。
为什么会发生变化?
因为相当于 a+= " world"; 相当于 new String(“hello world”) ;
此时a指向了新创建的字符串对象。
在这里插入图片描述

Stirng面试题

String newS1 = new String("hello");
String newS2 = new String("hello");
String ss1 = "hello";
String ss2 = "hello";
System.out.println("ss1==ss2是"+(ss1==ss2));
System.out.println("ss1==newS1是"+(ss1==newS1));
System.out.println("newS1==newS2是"+(newS1==newS2));

true
false
false

img

String newS1 = new String(“hello”)首先会在堆中创建一块内存, 内存地址返回给栈中newS1。

然后因为"hello"是常量,JVM会去方法区中的字符串常量池查看是否有"hello"字符串的对象,没有的话就分配一个空间来存放"hello",并将其空间地址存入堆中new出来的对象中;

在新建newS2对象时先去方法区的字符串常量池中寻找"hello"字符串对象,发现已经有了,就直接将其内存地址存入堆中new出来的对象中,同理堆中new出来的对象的内存地址返回给newS2

注意 newS1 和 newS2 使用 == 比较的是内存地址值

对于String ss1=“hello"这样创建的对象,JVM会直接检查字符串常量池是否已有"hello"字符串对象,如没有,就分配一个内存存放"hello”,如有了,则直接将字符串常量池中的地址返回给栈中的ss1而不经过堆中new的对象

所以ss1和ss2指向的是同一个对象。

这个时候有人就会有疑问了,既然ss1和ss2指向的是同一个对象,那我修过ss1的值岂不是也同时改变了ss2的值?

显然,改变ss1的值并不会影响ss2以及其它对象的值,原因其实Java的String是一个不可变的类,假设我们对ss1重新赋值时其实是在字符串常量池中重新分配了一块内存然后令ss1指向新对象

我们可以尝试用反射来强行修改字符串常量池中对象的值,来看看会怎样

String内部是由一个char[] value和int hash值构成,但是Java,下面用反射来改变其内部的value值

package 反射修改String;

import java.lang.reflect.Field;

public class 反射修改String {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String newS1 = new String("hello");
        String newS2 = new String("hello");
        String ss1 = "hello";
        String ss2 = "hello";
        System.out.println("ss1==ss2是" + (ss1 == ss2));
        System.out.println("ss1==newS1是" + (ss1 == newS1));
        System.out.println("newS1==newS2是" + (newS1 == newS2));

        //获取String类中的value字段
        Field valueFieldOfString = String.class.getDeclaredField("value");

        //改变value属性的访问权限
        valueFieldOfString.setAccessible(true);

        //获取ss1对象上的value属性的值
        char[] value = (char[]) valueFieldOfString.get(ss1);

        //把value所引用的数组中的第5个字符为'e'
        value[4] = 'e';

        System.out.println("ss1=" + ss1);
        System.out.println("ss2=" + ss2);
        System.out.println("newS1=" + newS1);
        System.out.println("newS2=" + newS2);
        System.out.println("ss1==ss2是" + (ss1 == ss2));
        System.out.println("ss1==newS1是" + (ss1 == newS1));
        System.out.println("newS1==newS2是" + (newS1 == newS2));

    }
}

ss1==ss2是true
ss1==newS1是false
newS1==newS2是false
ss1=helle
ss2=helle
newS1=helle
newS2=helle
ss1==ss2是true
ss1==newS1是false
newS1==newS2是false

结果所有最终指向字符串常量池为"hello"的对象最终都会变成"helle"。

其内存原理如下:

img

用反射强行把方法区中的"hello"中的’o’改成了’e’,所以最终指向同一个字符串常量池中的对象的值都会跟着改变

拆箱与装箱

//自动装箱 把基本类型转换为包装类类型
//相当于Integer.valueOf(99);
Integer total = 99; 
//自动拆箱 把包装类类型转换为基本类型 
//相当于total.intValue();
int totalprim = total;

Integer缓冲池

Integer的数据直接赋值

如:Integer a = 99;

相当于Integer.valueOf(99)。

如果在-128到127之间,会直接从缓冲池里获取数据。

package com.itheima.zkDemo;

public class TestInteger {

    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 1;
        System.out.println(a == b);

        Integer a1 = new Integer(1);
        Integer b1 = new Integer(1);
        System.out.println(a1 == b1);

        Integer a2 = Integer.valueOf(1);
        Integer b2 = Integer.valueOf(1);
        System.out.println(a2 == b2);
    }
}
//true
//false
//true
package com.itheima.zkDemo;

public class TestInteger2 {

    public static void main(String[] args) {

        Integer a = 129;
        Integer b = 129;
        System.out.println(a == b);

        Integer a1 = new Integer(129);
        Integer b1 = new Integer(129);
        System.out.println(a1 == b1);

        Integer a2 = Integer.valueOf(129);
        Integer b2 = Integer.valueOf(129);
        System.out.println(a2 == b2);
    }
}


//false
//false
//false

基本类型回收时机

我们要看你这个int a处于什么地方,如果是在方法内部中被执行到,也就是说是局部变量,就会在栈内存中分配内存,由于是int型,所以就是4字节,也就是32bit,long类型的才会是64bit。

​ 而你说的表示地址我们称为堆内存。创建的对象以及对象中的方法和数据是存储在堆内存中的。JVM会在栈内存中建立对象的引用,然后当执行到new语句时,在堆内存中创建对象,这时就将这个对象的类型以及这块区域的内存地址值赋给引用,然后进行对象中数据的初始化。

​ 也就是说,对象的引用存储在栈内存中,存放的是类型以及hash值,如Integer@0xff67。而对象里的内容实际上是存储在堆内存中的。
如果你这句int a只是在某个对象内作为成员变量,那么根本不会涉及到栈内存。a就存储在它所在的那个对象的堆内存中。明白了么?

对象数组

**数组既可以存储基本数据类型,也可以存储引用类型。**它存储引用类型的时候的数组就叫对象数组。

集合只能存储引用类型不能存储基本类型,具体表现为泛型为基本类型时编译期就会报错。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u44czF0r-1669712210994)(assets/1629514797676.png)]

排序算法

冒泡排序算法

相邻元素两两比较,大的往后放,第一次完毕,最大值出现在了最大索引处

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l4w0nfsX-1669712210994)(…/1.java%E5%9F%BA%E7%A1%80/assets/1615802790029.png)]

N个元素外层需要比较N-1次。内层每次需要比较N-1-外层层数。

假设4个元素,外层需要比较3次,内层每次需要比较3-外层层数

package digitalwallet;

public class CompareSort {
    public static void main(String[] args) {
        //int[] values = {80, 10, 20, 5, 8, 4, 9, 19, 90};
        int[] values = {80, 10, 20, 90};
        //冒泡排序代码
        bubbleSort(values);
        printArray(values);
    }

    public static void bubbleSort(int[] arr){
        boolean over = true;
        //假设有4个元素 需要两两比较 x<3 取值范围是 0 1 2 外层需要比较3 次
        for (int x = 0; x < arr.length - 1 && over; x++) {
            over = false;
            // y < 4 - 1 -x  x取值范围是 0 1 2
            // 即 y < 3 y < 2 y < 1 内层别需要比较 3 2 1
            for (int y = 0; y < arr.length - 1 - x; y++) {
                if (arr[y] > arr[y + 1]) {
                    int temp = arr[y];
                    arr[y] = arr[y + 1];
                    arr[y + 1] = temp;
                    over = true;
                }
            }
        }
    }

    // 遍历功能
    public static void printArray(int[] arr) {
        System.out.print("[");
        for (int x = 0; x < arr.length; x++) {
            if (x == arr.length - 1) {
                System.out.print(arr[x]);
            } else {
                System.out.print(arr[x] + ", ");
            }
        }
        System.out.println("]");
    }
}

package digitalwallet;

import sun.awt.SunHints;

public class CompareAndSort {
    public static void main(String[] args) {
        int[] values = {80, 10, 20, 90};

        for (int i = 0; i < values.length-1; i++) {
            for (int j = 0; j < values.length-1-i; j++) {
               int prev = values[j];
                int next = values[j+1];
                if(prev>next){
                    int temp = prev;
                    //注意这里不能使用prev 和 next 因为不是引用类型
                    values[j] = values[j+1];
                    values[j+1] =temp;
                }
            }
        }

        for (int i = 0; i < values.length; i++) {
            System.out.println(values[i]);
        }
    }

}

选择排序算法

从0索引开始,依次和后面元素比较,小的往前放,第一次完毕,最小值出现在了最小索引处

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSKxeiTa-1669712210995)(…/1.java%E5%9F%BA%E7%A1%80/assets/1615806876790.png)]

N个元素外层需要比较N-1次。内层每次需要比较N-1-外层层数。

假设4个元素,外层需要比较3次,内层每次需要比较4-外层层数-1 次,第一个元素分别和第二三四个元素比较共比较3次

public class ChooseCompare {
    public static void main(String[] args) {
        int[] values = {80, 10, 20, 90};
        //外层需要比较3次
        for (int i = 0; i < values.length-1; i++) {
            //内层需要比较 下标为0 的要和 下标为1的比较 和 下标为1 2 3的比较 总共 比较3次
            for (int j = i+1; j < values.length; j++) {
                if (values[i]>values[j]){
                    int temp = values[j];
                    values[j] =values[i];
                    values[i] = temp;
                }
            }
        }
        for (int value : values) {
            System.out.println(value);
        }
    }
}

===

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值