5 个最常见的Java陷阱
1.不理解String是不可变类:
Java String 类是不可变的(Unmodifiable) 。这是因为字符串对象缓存在字符串池中。 String 引用的对象可以更改,但 String 对象本身不能。
例子:
Java
public class Main {
public static void main(String[] args)
{
String str = "GeeksFor";
// A new string will be returned, but the actual String will remain the same
str.concat("Geeks");
// Prints initial value "GeeksFor"
System.out.println(str);
// Now we change the reference of "str" to point to the new String returned
str = str.concat("Geeks");
// Prints the new concatenated String
System.out.println(str);
}
}
Java
public class Main {
public static void main(String[] args)
{
int num = 0;
/* First case */
// The increment operator happens after the value is pushed onto the stack and assigned
num = num++;
// Prints initial value
System.out.println(num);
/* Second case */
// Increment occurs first, and then it is pushed to the stack and assigned to num
num = ++num;
System.out.println(num);
}
}
Java
public class Main {
public static void main(String[] args)
{
String s1 = new String("GeeksForGeeks");
String s2 = new String("GeeksForGeeks");
// Comparing using the relational operator
if (s1 == s2) { // false
System.out.println("Both objects point to the same memory location!");
}
// Comparing using the .equals()
if (s1.equals(s2)) { // true
System.out.println("The value inside the object instances match!");
}
// Declaring a string with a reference to s1
String s3 = s1;
if (s3 == s1) { // true
System.out.println("Both objects point to the same memory location!");
}
}
}
Java
public class Main {
public static void main(String[] args)
{
String s1 = "GeeksForGeeks";
String s2 = "GeeksForGeeks";
// Comparing using the relational operator
if (s1 == s2) { // true
System.out.println("The two strings are the same!");
}
else {
System.out.println("The two strings are the different!");
}
}
}
Java
public class Main {
public static void main(String[] args)
{
// Defining a raw list, without specifying its type parameter
List geeksList = new ArrayList();
// Due to the unspecified type we can add any data type and the code will compile
geeksList.add(100);
geeksList.add(200);
geeksList.add("GeeksForGeeks");
// When the second element is reached the compiler will throw a runtime error
// because we are trying to cast a string to an integer
geeksList.forEach(k -> System.out.println((int)k * 2));
}
}
Output : GeeksFor
GeeksForGeeks
2. 内存泄漏:
Java的核心优势之一是Java垃圾收集器,它管理堆上的对象内存。每当一个对象不可达时,它就会自动释放。
然而,对于新手和有经验的程序员来说,一个常见的失误是通过允许不再使用的对象可以访问来阻止内存被释放。这对项目来说可能是一个非常大的缺点,因为内存泄漏会阻塞资源并降低应用程序性能。它甚至会导致Java.lang.OutOfMemoryError 。
常见情况有:
- 静态字段声明静态字段并在不再需要其数据后忘记将其设置为空
- 未关闭的流Java虚拟机为每个打开的连接分配内存。忘记关闭连接会消耗内存。此类连接可以是:输入流、数据库连接、会话等。
- finalize()方法当我们重写finalize()方法时,该类的对象不再直接被垃圾回收。然后垃圾收集器等待最终确定,这将在稍后的时间点发生。
3. Pre/Post Increment Operator 和副作用:
Java中运算符的求值顺序似乎是从左到右求值的,可以立即看到副作用:
Java
public class Main {
public static void main(String[] args)
{
int num = 0;
/* First case */
// The increment operator happens after the value is pushed onto the stack and assigned
num = num++;
// Prints initial value
System.out.println(num);
/* Second case */
// Increment occurs first, and then it is pushed to the stack and assigned to num
num = ++num;
System.out.println(num);
}
}
Output : 0
1
第一种情况的执行上下文如下:
- 存储操作数的先前值。
- 增加值。
- 返回上一个值
第二种情况的执行上下文如下:
- 增加值。
- 存储操作数的值(递增)
- 返回值
4. 使用关系运算符“==”进行对象比较。
许多新手程序员尝试使用“==”运算符来比较对象,当他们的代码行为与预期不符时,他们会感到困惑。需要注意的是关系运算符“==”正在进行引用比较,它检查两个对象是否指向内存中的相同位置。使用.equals()方法将消除问题,因为它比较对象内部的值。
Java
public class Main {
public static void main(String[] args)
{
String s1 = new String("GeeksForGeeks");
String s2 = new String("GeeksForGeeks");
// Comparing using the relational operator
if (s1 == s2) { // false
System.out.println("Both objects point to the same memory location!");
}
// Comparing using the .equals()
if (s1.equals(s2)) { // true
System.out.println("The value inside the object instances match!");
}
// Declaring a string with a reference to s1
String s3 = s1;
if (s3 == s1) { // true
System.out.println("Both objects point to the same memory location!");
}
}
}
Output : The value inside the object instances match!
Both objects point to the same memory location!
虽然有时“==”运算符会给出预期的答案:
Java
public class Main {
public static void main(String[] args)
{
String s1 = "GeeksForGeeks";
String s2 = "GeeksForGeeks";
// Comparing using the relational operator
if (s1 == s2) { // true
System.out.println("The two strings are the same!");
}
else {
System.out.println("The two strings are the different!");
}
}
}
Output : The two strings are the same!
原因在于Java语言规范中的字符串字面量:'同一包中同一类中的字面量字符串表示对同一字符串对象的引用'。代码中的条件为真,因为字面量由相同的字符组成。
5. 使用原始类型
在Java开始使用泛型类型之前,还没有原始类型(未参数化的类型)的替代品。出于向后兼容性的原因,仍然可以定义这些:
Java
public class Main {
public static void main(String[] args)
{
// Defining a raw list, without specifying its type parameter
List geeksList = new ArrayList();
// Due to the unspecified type we can add any data type and the code will compile
geeksList.add(100);
geeksList.add(200);
geeksList.add("GeeksForGeeks");
// When the second element is reached the compiler will throw a runtime error
// because we are trying to cast a string to an integer
geeksList.forEach(k -> System.out.println((int)k * 2));
}
}
Output : 200
400
Exception in thread "main" java.lang.ClassCastException:
java.base/java.lang.String cannot be cast to java.base/java.lang.Integer
可以通过定义 List 的泛型类型来防止此问题:
List geeksList = new ArrayList();
现在代码将无法编译,因为我们试图将 String 类型添加到 Integer 类型的集合中。创建泛型类型是有原因的,可以防止程序员出现讨厌的错误和开销。