hashCode 与 equals
1、hashCode()介绍
hashCode()的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
2、为什么要有 hashCode
hashCode是一个用于快速查找对象的整数值,它可以用于哈希表、集合等数据结构中,以提高数据的查找效率。hashCode方法是一种散列码算法,它可以将任意长度的输入数据映射到固定长度的散列码中,从而方便对数据进行比较和查找
例如:当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同, HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
3、两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?
1、如果两个对象相等,则hashcode一定也是相同的
2、两个对象相等,对两个对象调用equals方法返回true
3、两个对象有相同的hashcode值,它们也不一定是相等的
4、为什么重写equals时必须重写hashCode方法?
如果只重写了 equals 方法,那么默认情况下,假设存了两个自定义的内容相同的对象到Set中,Set 进行去重操作时,会先判断两个对象的 hashCode 是否相同,此时因为没有重写 hashCode 方法,所以会直接执行 Object 中的 hashCode 方法,而 Object 中的 hashCode 方法对比的是两个不同引用地址的对象,那么得到的两个Hash值是不一样的,那么 equals 方法就不会执行了,这两个对象就会被判断为不是相等的,于是就在 Set 集合中插入了两个相同的对象(这里的相同是指的内容相同)。
但是,如果在重写 equals 方法时,也重写了 hashCode 方法,那么在执行判断时会去执行重写的 hashCode 方法,此时对比的是两个对象的所有属性的 hashCode 是否相同,于是调用 hashCode 返回的结果就是 true,再去调用 equals 方法,发现两个对象确实是相等的,于是就返回 true 了,因此 Set 集合就不会存储两个一模一样的数据了,于是整个程序的执行就正常了。
总结
hashCode 和 equals 两个方法是用来协同判断两个对象是否相等的,采用这种方式的原因是可以提高程序插入和查询的速度,如果在重写 equals 时,不重写 hashCode,就会导致在某些场景下,例如将两个相等的自定义对象存储在 Set 集合时,就会出现程序执行的异常,为了保证程序的正常执行,所以我们就需要在重写 equals 时,也一并重写 hashCode 方法才行。
对象的相等与指向他们的引用相等,两者有什么不同?
在Java中,对象的相等与指向它们的引用相等是两个不同的概念,它们有以下几点不同:
1. 比较方式不同:对象的相等通常使用equals方法进行比较,而指向它们的引用相等通常使用“==”运算符进行比较。
2. 比较结果不同:对象的相等比较的是对象的内容是否相同,而指向它们的引用相等比较的是对象的内存地址是否相同。
3. 对象类型不同:对象的相等适用于所有对象类型,包括基本数据类型和对象类型,而指向它们的引用相等只适用于对象类型。
需要注意的是,如果一个类没有重载equals方法,那么它将继承Object类的equals方法,该方法比较的是两个对象的内存地址是否相同,即与指向它们的引用相等的结果相同。
通俗易懂的讲解为什么 Java 中只有值传递
在Java中,只有值传递,这是因为Java中的变量在传递给方法时,实际上是将变量的值(即数据的拷贝)传递给方法,而不是将变量本身传递给方法。
可以将变量想象成一个盒子,盒子里装着数据。当一个变量作为参数传递给方法时,实际上是将盒子里的数据复制一份,并将这份数据传递给方法。这就好比你把一个盒子里的东西复制一份给朋友,朋友拿到的是复制品,而不是原来的盒子。同理,方法也拿到的是变量的复制品,而不是原来的变量。
需要注意的是,如果变量是引用类型的变量,那么复制的是变量的值(即内存地址),而不是对象本身。这意味着,如果在方法内部修改了对象的数据,那么在方法外部也能看到这些修改,因为它们都是指向同一个对象的。
总之,Java中只有值传递,因为变量在传递给方法时,实际上是将变量的值(即数据的拷贝)传递给方法,而不是将变量本身传递给方法。因此,在Java中不能直接实现引用传递,但是可以通过修改对象的数据来达到类似于引用传递的效果。
骚戴理解:java中只要值传递,你传递的变量如果是引用类型,那也是把这个变量的内存地址复制一份传过去,重点就在于这里是复制!!!虽然我传递的是内存地址,但是我采用的方法是复制这个内存地址再把复制的传过去,效果是和引用传递一样,但是从定义上来看,由于是复制一份内存地址传递,所以就是值传递!
值传递和引用传递有什么区别
值传递和引用传递是两种不同的参数传递方式,它们的区别主要在于传递的是变量的值还是变量的引用(内存地址)。
在值传递中,当一个变量作为参数传递给方法时,实际上是将该变量的值(即数据的拷贝)传递给方法,而不是将变量本身传递给方法。在方法内部对该变量进行修改不会影响到原来的变量,因为它们指向的是不同的内存地址。这就好比你把一个盒子复制一份给朋友,朋友拿到的是复制品,而不是原来的盒子。
在引用传递中,当一个变量作为参数传递给方法时,实际上是将该变量的引用(内存地址)传递给方法,而不是将变量的值传递给方法。在方法内部对该变量进行修改会影响到原来的变量,因为它们指向的是同一个内存地址。这就好比你把一个盒子里的东西给朋友,朋友拿到的是原来的盒子,你和朋友都可以修改盒子里的东西。
JDK 中常用的包有哪些
JDK中常用的包有很多,其中一些常用的包如下:
1. java.lang:包含Java语言的核心类,如基本数据类型、字符串、异常处理等。
2. java.util:包含各种实用工具类,如集合框架、日期和时间处理、随机数生成、正则表达式等。
3. java.io:包含输入输出相关的类,如文件读写、网络通信、对象序列化等。
4. java.net:包含网络编程相关的类,如Socket、URL等。
5. java.awt:包含图形用户界面(GUI)相关的类,如窗口、按钮、文本框等。
6. javax.swing:是AWT的扩展,提供了更多的GUI组件、更好的外观和感觉、更好的事件处理等。
7. java.sql:包含与数据库相关的类,如连接数据库、执行SQL语句、处理结果集等。
8. java.math:包含高精度计算相关的类,如BigDecimal、BigInteger等。
9. java.security:包含与安全相关的类,如加密、数字签名、密钥管理等。
10. java.text:包含文本处理相关的类,如格式化和解析日期、数字、货币等。
除了以上这些常用的包之外,JDK中还有很多其他的包,如XML处理、国际化、反射等。开发者在编写Java程序时需要根据具体的需求选择合适的包
IO流
反射
什么是反射机制?
反射是Java语言的一种机制,它允许程序在运行时获取对象的类型信息、访问对象的属性和方法,并调用对象的方法。反射机制使得程序可以在运行时动态地创建对象、调用方法、访问属性等,而不需要在编译时确定这些信息。
在Java中,每个类都有一个Class对象,它包含了该类的所有信息,如类名、属性、方法等。通过反射机制,可以获取一个类的Class对象,然后使用该对象来访问类的属性和方法。反射机制可以让程序在运行时动态地创建对象、调用方法、访问属性等,而不需要在编译时确定这些信息。
反射机制在Java中有很多应用,如动态代理、注解处理、框架开发等。但是,由于反射机制需要在运行时动态地获取对象信息,所以会带来一定的性能损失。因此,在使用反射机制时需要注意性能问题,并合理使用缓存等技术来提高程序的效率。
反射机制优缺点
反射机制是Java语言的一种特性,它允许程序在运行时动态地获取对象的类型信息、访问对象的属性和方法,并调用对象的方法。反射机制的优缺点如下:
优点:
1. 动态性:反射机制允许程序在运行时动态地获取对象的类型信息、访问对象的属性和方法,并调用对象的方法,从而使程序具有更大的灵活性和动态性。
2. 扩展性:反射机制可以让程序在运行时动态地创建对象、调用方法、访问属性等,而不需要在编译时确定这些信息,从而使程序具有更好的扩展性。
3. 适用性:反射机制可以应用于很多场景,如动态代理、注解处理、框架开发等,使程序更加灵活和可扩展。
缺点:
1. 性能问题:反射机制需要在运行时动态地获取对象信息,所以会带来一定的性能损失,特别是在频繁调用的情况下,会对程序的性能产生较大的影响。
2. 安全问题:反射机制可以访问对象的私有属性和方法,从而可能破坏程序的安全性。
3. 可读性问题:反射机制使得程序的逻辑更加复杂,代码可读性较差,特别是对于初学者来说,可能会造成困难。
总之,反射机制是Java语言的一种特性,它具有动态性、扩展性和适用性等优点,但也存在性能、安全和可读性等缺点。在使用反射机制时需要权衡其优缺点,并根据具体情况进行选择。
反射机制的应用场景有哪些?
反射机制在Java中有很多应用场景,包括以下几个方面:
1. 动态代理:通过反射机制,可以动态地生成代理对象,从而实现对目标对象的代理操作,如AOP、RPC等。
2. 注解处理:通过反射机制,可以读取和处理注解信息,从而实现对程序的自动化处理,如自动化测试、代码生成等。
3. 框架开发:通过反射机制,可以实现框架的扩展性和灵活性,如Spring框架中的BeanFactory、AOP、IOC等。Spring 通过 XML 配置模式装载 Bean 的过程(根据配置文件里的bean定义,通过反射创建一个bean对象)
4. 反射调试工具:通过反射机制,可以实现反射调试工具,如获取对象的类名、属性、方法等信息,从而方便程序员进行调试和开发。我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;
5. 序列化和反序列化:通过反射机制,可以实现对象的序列化和反序列化,从而实现对象的持久化和传输。
6. 动态加载类和资源:通过反射机制,可以动态地加载类和资源,从而实现程序的动态扩展和更新。
Java获取反射的三种方法
- 通过对象实例的getClass方法获取 (使用的最少的)
- 通过Class.forName("路径")实现反射机制 (jdbc驱动)
- 通过类名.class来实现反射机制
public class Student{ private int id; String name; protected boolean sex; public float score; } public classGet{ //获取反射机制三种方式 public static void main(String[] args) throws ClassNotFoundException { //方式一(通过建立对象) Student stu =new Student(); Class classobj1= stu.getClass(); System.out.println(classobj1.getName()); //方式二(所在通过路径-相对路径) Class classobj2= Class.forName("fanshe.Student"); System.out.println(classobj2.getName()); //方式三(通过类名) Class classobj3= Student.class; System.out.println(classobj3.getName()); } }
String
字符型常量和字符串常量的区别
字符型常量和字符串常量是Java中的两种常量类型,它们的区别如下:
1. 字符型常量:字符型常量是用单引号括起来的单个字符,如'a'、'b'、'c'等。字符型常量只能包含一个字符,且必须用单引号括起来。
2. 字符串常量:字符串常量是用双引号括起来的一串字符,如"Hello"、"World"等。字符串常量可以包含多个字符,且必须用双引号括起来。
3. 存储方式:字符型常量在内存中以字符的形式存储,而字符串常量在内存中以字符数组的形式存储。
4. 可变性:字符型常量是不可变的,一旦定义,就不能修改。而字符串常量可以通过各种方法进行修改,如拼接、替换、截取等。
5. 比较方式:字符型常量可以使用==运算符进行比较,比较的是字符的ASCII码值。而字符串常量不能使用==运算符进行比较,需要使用equals()方法或compareTo()方法进行比较。
什么是字符串常量池?
字符串常量池是Java中的一种特殊的内存区域,用于存储字符串常量,以便重复使用。当程序创建一个字符串常量时,如果该字符串常量在常量池中已经存在,则直接返回该字符串常量的引用;如果该字符串常量在常量池中不存在,则将该字符串常量添加到常量池中,并返回该字符串常量的引用。
字符串常量池的优点是可以减少内存的使用,提高程序的性能。因为字符串常量池会缓存字符串常量,以便重复使用,所以可以避免创建重复的字符串常量,从而减少内存的使用。同时,由于字符串常量池是一种特殊的内存区域,它的访问速度比堆内存和栈内存更快,所以可以提高程序的性能。
需要注意的是,字符串常量池中的字符串常量是不可变的,一旦定义,就不能修改。如果程序需要修改字符串常量,应该使用StringBuilder或StringBuffer等可变的字符串类型。
jdk1.8的字符串常量池是在堆内存中吗?
在JDK1.8中,字符串常量池的位置是在堆内存中,但是和一般的堆内存不同,它是被划分出来的一块特殊的内存区域,称为"永久代"(Permanent Generation),也叫"元空间"(Metaspace)。
在JDK1.8之前,字符串常量池是在方法区中的,但是在JDK1.8之后,方法区被移除了,取而代之的是Metaspace。Metaspace是一种与堆内存分离的内存区域,用于存储类的元数据、静态变量、常量等数据。在Metaspace中,字符串常量池也被移动到了堆内存中。
需要注意的是,在JDK1.8中,字符串常量池也有一些变化。在JDK1.8之前,字符串常量池是固定大小的,当常量池中的字符串对象过多时,会导致OutOfMemoryError错误。但是在JDK1.8中,字符串常量池的大小是不固定的,可以动态调整,避免了OutOfMemoryError错误的问题。同时,JDK1.8中也引入了一种新的字符串常量池实现方式,称为"字符串去重"(String Deduplication),可以进一步减少字符串常量池的内存占用。
String 是最基本的数据类型吗?
不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(referencetype),Java 5 以后引入的枚举类型也算是一种比较特殊的引用类型。
这是很基础的东西,但是很多初学者却容易忽视,Java 的 8 种基本数据类型中不包括 String,基本数据类型中用来描述文本数据的是 char,但是它只能表示单个字符,比如 ‘a’,‘好’ 之类的,如果要描述一段文本,就需要用多个char 类型的变量,也就是一个 char 类型数组,比如“你好” 就是长度为2的数组 char[] chars = {‘你’,‘好’};
但是使用数组过于麻烦,所以就有了 String,String 底层就是一个 char 类型的数组,只是使用的时候开发者不需要直接操作底层数组,用更加简便的方式即可完成对字符串的使用。
简单来说就是String类利用了final修饰的char类型数组存储字符,源码如下所示:
private final char value[];
String有哪些特性
String是Java中的一个类,具有以下特性:
1. 不可变性:String对象一旦创建,就不能被修改。如果需要修改字符串内容,只能创建一个新的String对象。这种不可变性使得String对象在多线程环境下更加安全。
2. 字符串常量池:Java中的字符串常量池是一种特殊的内存区域,用于缓存字符串常量,以便重复使用。String类中的字符串常量都是在字符串常量池中创建的。
3. 比较方式:String类中提供了多种比较字符串的方法,如equals()、equalsIgnoreCase()、compareTo()等。其中,equals()方法比较的是字符串的内容,而==运算符比较的是字符串对象的引用。
4. 不可变哈希值:String类中的哈希值是不可变的,一旦计算出来就不能被修改。这种不可变性使得String对象可以作为Map的key来使用。
5. 字符串操作方法:String类中提供了多种字符串操作方法,如substring()、trim()、toUpperCase()、toLowerCase()等,可以方便地对字符串进行操作和处理。
6. 国际化支持:String类中提供了多种支持国际化的方法,如getBytes()、charAt()、length()等,可以处理不同语言和编码的字符串。
String为什么是不可变的吗?
String是不可变的,主要是为了保证程序的安全性和性能。
1. 安全性:如果String是可变的,那么在多线程环境下,多个线程可能会同时修改同一个String对象,从而导致数据不一致的问题。为了避免这种问题,Java将String设计为不可变的,一旦创建,就不能被修改,这样就可以避免多线程环境下的数据竞争问题。
2. 性能:如果String是可变的,那么每次修改字符串都需要创建一个新的String对象,这样会导致频繁的对象创建和销毁,从而降低程序的性能。为了避免这种问题,Java将String设计为不可变的,一旦创建,就不需要再修改,这样可以避免频繁的对象创建和销毁,提高程序的性能。
另外,String是不可变的还有一个好处,就是可以作为Map的key来使用。由于String的哈希值是不可变的,一旦计算出来就不能被修改,所以String对象可以作为Map的key来使用,保证了Map的稳定性和正确性。如果String是可变的,就不能作为Map的key来使用,从而限制了Map的使用范围。
综上所述,String是不可变的,主要是为了保证程序的安全性和性能。
是否可以继承 String 类
String 类是 final 类,不可以被继承。
String str="i"与 String str=new String(“i”)一样吗?
在功能上,String str="i"与String str=new String("i")是相同的,都是创建一个包含单个字符i的字符串对象。但是,在内存中,它们是不同的。
String str="i"是使用字符串常量池中的字符串常量创建的,而字符串常量池是Java中的一种特殊的内存区域,用于缓存字符串常量,以便重复使用。当程序创建一个字符串常量时,如果该字符串常量在常量池中已经存在,则直接返回该字符串常量的引用;如果该字符串常量在常量池中不存在,则将该字符串常量添加到常量池中,并返回该字符串常量的引用。因此,使用String str="i"创建的字符串对象会被添加到字符串常量池中,如果创建多个包含相同内容的字符串对象,只会在字符串常量池中创建一个对象,可以节省内存空间。
而String str=new String("i")是使用new关键字创建的字符串对象,每次都会在堆内存中创建一个新的对象,不会使用字符串常量池中的对象。如果创建多个包含相同内容的字符串对象,每个对象都会在堆内存中创建一个新的对象,会占用更多的内存空间。
因此,如果需要创建包含单个字符的字符串对象,建议使用String str="i"的方式,可以避免在堆内存中创建多个相同的对象,节省内存空间。
String s = new String(“xyz”);创建了几个字符串对象
使用String s = new String("xyz")创建了两个字符串对象。
第一个字符串对象是字面量"xyz",它是在编译期间就被创建并存储在字符串常量池中的。当程序执行到String s = new String("xyz")这句话时,会在堆内存中创建一个新的String对象,该对象的内容是字符串常量池中"xyz"字符串对象的拷贝。因此,使用new关键字创建String对象时,会在堆内存中创建一个新的对象。
总之,使用String s = new String("xyz")创建了两个字符串对象,一个是字符串常量池中的"xyz"字符串对象,另一个是堆内存中的新对象,该对象的内容是字符串常量池中"xyz"字符串对象的拷贝。需要注意的是,如果在代码中多次使用相同的字符串字面量,只会在字符串常量池中创建一个字符串对象,不会重复创建。
String str1 ="hello";//str1指向静态区(字符串常量池) String str2 =new String("hello");//str2指向堆上的对象 String str3 ="hello"; String str4=new String("hello"); 1、System.out.println(str1.equals(str2));//true 2、System.out.println(str2.equals(str4));//true 3、System.out.println(str1== str3);//true 4、System.out.println(str1 == str2);//false 5、System.out.println(str2 == str4);//false 6、System.out.println(str2=="hello");//false str2 = str1; 7、System.out.println(str2 =="hello");//true
骚戴理解:首先String的equals方法是被重写过的,比较的内容是否相同,所以序号1和2都是true,因为内容都是hello,但是"=="在比较引用类型的时候比较的是内存地址是否相同,因为str1和str3都是指向的字符串常量池里hello这个字符串的地址,所以序号3位true,但是序号4、5、6都是false,因为它们内存地址都不一样,str2 = str1把str2指向的地址改成了指向字符串常量池中hello的地址后自然序号7为true
如何将字符串反转?
使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
示例代码:
// StringBuffer reverse StringBuffer stringBuffer = new StringBuffer(); stringBuffer. append("abcdefg"); System. out. println(stringBuffer. reverse());
结果: gfedcba
// StringBuilder reverse StringBuilder stringBuilder = new StringBuilder(); stringBuilder. append("abcdefg"); System. out. println(stringBuilder. reverse());
结果: gfedcba
数组有没有 length()方法?String 有没有 length()方法
数组没有length()方法,但是有一个length属性,可以用来获取数组的长度,或者用 size方法。
例如,对于int类型的数组,可以使用以下方式获取数组的长度:
int[] arr = {1, 2, 3, 4, 5}; int len = arr.length; // 获取数组的长度,结果为5
对于String类型的对象,有一个length()方法,可以返回字符串的长度,即字符串中字符的个数。
例如,可以使用以下方式获取字符串的长度:
String str = "Hello World"; int len = str.length(); // 获取字符串的长度,结果为11
需要注意的是,String的length()方法返回的是字符串中字符的个数,而不是字节数或者编码单元数。对于包含Unicode字符的字符串,其长度可能会大于字符串的字节数或编码单元数。
String 类的常用方法都有那些?
String类是Java中最常用的类之一,提供了许多实用的方法,下面列出了String类的一些常用方法:
1. charAt(int index):返回指定索引处的字符。
2. length():返回字符串的长度,即字符串中字符的个数。
3. substring(int beginIndex):返回从指定索引开始到字符串末尾的子字符串。
4. substring(int beginIndex, int endIndex):返回从指定索引开始到指定索引结束的子字符串。
5. equals(Object obj):比较字符串是否相等,区分大小写。
6. equalsIgnoreCase(String anotherString):比较字符串是否相等,忽略大小写。
7. compareTo(String anotherString):按字典顺序比较两个字符串,如果相等返回0,如果当前字符串大于另一个字符串返回正数,否则返回负数。
8. toLowerCase():将字符串中的所有字符转换为小写。
9. toUpperCase():将字符串中的所有字符转换为大写。
10. trim():去除字符串开头和结尾的空格。
11. startsWith(String prefix):判断字符串是否以指定的前缀开始。
12. endsWith(String suffix):判断字符串是否以指定的后缀结束。
13. indexOf(int ch):返回指定字符在字符串中第一次出现的索引。
14. lastIndexOf(int ch):返回指定字符在字符串中最后一次出现的索引。
15. replace(char oldChar, char newChar):将字符串中的所有旧字符替换为新字符。
16. replaceAll(String regex, String replacement):将字符串中所有匹配正则表达式的子串替换为指定字符串。
17. split(String regex):按照指定的正则表达式将字符串拆分为多个子串。
18. valueOf(Object obj):将指定的对象转换为字符串。
19. format(String format, Object... args):使用指定的格式字符串和参数返回格式化的字符串。
在使用 HashMap 的时候,用 String 做 key 有什么好处?
在使用HashMap时,使用String作为key有以下好处:
1. String是不可变的,因此可以保证key的不可变性,避免了在修改key时可能出现的问题。
2. String的hashCode()方法被重写过,可以保证相同字符串的hashCode值相同,因此可以更好地利用HashMap的散列特性。
3. String实现了Comparable接口,可以进行比较,因此在对HashMap进行排序或者遍历时,可以更方便地进行操作。
4. String是Java中最常用的类之一,使用String作为key可以提高代码的可读性和可维护性。
骚戴理解:需要注意的是,在使用自定义类作为HashMap的key时,需要重写hashCode()和equals()方法,以保证正确性和性能。
String和StringBuffer、StringBuilder的区别是什么?
String、StringBuffer和StringBuilder都是Java中用于处理字符串的类,它们的主要区别如下:
1. String是不可变的,即一旦创建就不能修改,对String对象进行修改实际上是创建了一个新的String对象,旧的String对象仍然存在。而StringBuffer和StringBuilder是可变的,可以修改其内容。
2. StringBuffer和StringBuilder都是可变的字符串缓冲区,它们的主要区别在于线程安全性。StringBuffer是线程安全的,即多个线程可以同时访问一个StringBuffer对象并执行修改操作,而不会产生不一致的结果。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder则是非线程安全的,如果多个线程同时访问一个StringBuilder对象并执行修改操作,可能会产生不一致的结果。String中的对象是不可变的,也就可以理解为常量,线程安全。
3. 在字符串拼接的场景中,如果使用String进行拼接,每次拼接都会创建一个新的String对象,如果拼接的字符串比较多,会产生大量的临时对象,占用大量内存,影响性能。而如果使用StringBuffer或StringBuilder进行拼接,由于它们是可变的,可以直接在原有对象上进行修改,避免了创建大量临时对象,提高了性能。
骚戴理解:性能由好到坏:StringBuilder>StringBuffer>String
总之,String、StringBuffer和StringBuilder都有各自的特点,需要根据具体的场景选择合适的类进行使用。如果需要进行频繁的字符串拼接操作,建议使用StringBuffer或StringBuilder,而不是使用String。如果需要在多线程环境下进行字符串操作,建议使用StringBuffer,而不是使用StringBuilder。
包装类
自动装箱与拆箱
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型;
int 和 Integer 有什么区别
它们的主要区别如下:
1. int是Java的基本数据类型,而Integer是int的包装类,是一个对象。
2. int在内存中占用4个字节,而Integer对象在内存中占用更多的空间。
3. int可以直接进行数值运算,而Integer对象需要通过intValue()方法将其转换为int类型后才能进行数值运算。
4. int是值类型,即在传递时传递的是实际的数值,而Integer是引用类型,即在传递时传递的是对象的引用。
5. 在使用时,int通常用于基本的数值运算,而Integer通常用于需要将int类型的数据封装成对象的场景,例如在集合类中存储int类型的数据。
总之,int和Integer都有各自的特点和使用场景,需要根据具体的需求进行选择和使用。需要注意的是,在进行数值运算时,尽量使用int,避免使用Integer对象,以提高性能。
Java 原始类型和包装类对比
Java 为每个原始类型提供了包装类型:
原始类型::boolean char byte short int long float double
包装类型:Boolean Character Byte Short Integer Long Float Double
Integer a= 127 与 Integer b = 127相等吗
相等
总结
int 和Integer在进行比较的时候,Integer会进行拆箱,转为int值与int进行比较。
Integer与Integer比较的时候,由于直接赋值的时候会进行自动的装箱,那么这里就需要注意两个问题
一是-128<= x<=127的整数,那么当赋值在这个区间的时候,不会创建新的Integer对象,而是从IntegerCache缓存中获取已经创建好的Integer对象。
二是当大于这个范围的时候,直接new Integer来创建Integer对象。
new Integer(1) 和Integer a = 1不同,前者会创建对象,存储在堆中,而后者因为在-128到127的范围内,不会创建新的对象,而是从IntegerCache中获取的。那么Integer a = 128, 大于该范围的话才会直接通过new Integer(128)创建对象,进行装箱。
对于对象引用类型:==比较的是对象的内存地址。对于基本数据类型:==比较的是值。
注意只有new Integer不一样点,不管new的这个数是不是在-128到127这个范围内都会在堆中创建一个新的对象,而不是使用IntegerCache缓存中的值
public class a { public static void main(String[] args){ Integer a =new Integer(3); Integer b =3;// 将 3 自 动 装 箱 成 Integer 类 型 int c =3; System.out.println(a == b);// false 两个引用没有引用同一对象 System.out.println(a == c);// true a自动拆箱成int类型再和c比较 System.out.println(b == c);// true } }
骚戴理解:Integer a =new Integer(3);是创一个对象在堆里, Integer b =3;是直接从IntegerCache缓存中拿(-128到127这个范围内的Integer对象已经在这个IntegerCache缓存中创建好了的),把b的引用指向IntegerCache缓存中的3。所以第一个判断为false。
第二个和第三个都是因为会拆箱,所以就是int之间的值比较,自然是true
package anomyous; public class Test { public static void main(String[] args) { Integer i1=new Integer(99); Integer i2=Integer.valueOf(99);//手动装箱 Integer i3=99;//自动装箱 隐式调用Integer.valueOf() System.out.println(i1==i2);//false System.out.println(i2==i3);//true System.out.println(i1==i3);//false } } public static void main(String[] args){ Integer a1 =128; Integer b1 =128; System.out.println(a1 == b1);// false Integer a2=127; Integer b2 =127; System.out.println(a2 == b2);// true }
骚戴理解:如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围 a1==b1的结果是false