一緒に創造し、成長するために一緒に働きましょう!「ナゲッツデイリー新プラン・8月アップデートチャレンジ」参加30日目、イベント詳細はこちら
バックグラウンド
私は長年ソフトウェア業界に携わっており、生産と開発の関連する落とし穴に足を踏み入れてきました.今日はそれを共有します.関連する経験を蓄積し、繰り返される落とし穴を避けることができることを願っています.
整数のオートボクシングとアンボクシング
例えば
public static void main(String[] args)
{
Integer x1 = 10;
Integer x2 = 10;
boolean r = x1==x2;
System.out.println("x1==x2:" + r);
Integer y1 = 300;
Integer y2 = 300;
boolean r1 = y1==y2;
System.out.println("y1==y2:" + r1);
Integer m1 = new Integer(300);
Integer m2 = new Integer(300);
boolean r2 = m1==m2;
System.out.println("m1==m2:" + r2);
Integer n1 = new Integer(20);
Integer n2 = new Integer(20);
boolean r3 = n1.intValue()==n2.intValue();
System.out.println("n1==n2:" + r3);
}
复制代码
説明: 整数のボックス化解除とボックス化の問題については、整数のデフォルトのキャッシュ範囲をマスターするだけで、これらの質問に簡単に答えることができます。Integer は int 型のラッパー クラスです. int 値が Integer に割り当てられると, valueOf(int i) メソッドを使用して自動的にボックス化されます. デフォルトでは, cache[] キャッシュ範囲は [-128,127] です.
文字列の == と euals の問題
String の == と equal の質問については、その年のインターンシップの面接で、最初の質問がこれで、結果が間違っていて、面接官は出て右に曲がると言いました。ですから、これはまだ記憶に新しいところです。
例えば
public static void main(String[] args)
{
//
String s1 = "abc";
String s2 = "abc";
System.out.println("s1 == s2 : " + (s1 == s2));
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println("s3 == s4 : " + (s3 == s4));
String s5 = "ab" + "cd";
String s6 = "abcd";
System.out.println("s5 = s5 : " + (s5 == s6));
String str1 = "ab";
String str2 = "cd";
String str3 = str1 + str2;
String str4 = "abcd";
System.out.println("str4 = str3 : " + (str3 == str4));
String str6 = "b";
String str7 = "a" + str6;
String str8 = "ab";
System.out.println("str7 = str8 : " + (str7 == str8));
//常量话
final String str9 = "b";
String str10 = "a" + str9;
String str11 = "ab";
System.out.println("str9 = str89 : " + (str10 == str11));
String s12="abc";
String s13 = new String("abc");
System.out.println("s12 = s13 : " + (s12 == s13.intern()));
}
复制代码
注: String の == と equal に関しては、String のメモリ モデルをマスターする必要があるため、上記の質問に正確に答えることができ、穴を踏むことを恐れません。
精度損失の問題
精度の損失の問題は、主に次の例のように、浮動小数点型と BigDecimal データに反映されます。
public class BigDecimalTest
{
public static void main(String[] args)
{
float a = 1;
float b = 0.9f;
System.out.println(a - b);
}
}
复制代码
0.1 のバイナリ表現は無限ループであるため、出力は 0.100000024 です。コンピュータのリソースは限られているため、0.1 を 2 進数で正確に表現する方法はなく、「近似値」でしか表現できません。精度の欠如につながります。
そのため、BigDecimal 型を使用して操作することもできますが、BigDecimal には精度が失われるという問題もあります。
BigDecimal c = new BigDecimal(1.0);
BigDecimal d =new BigDecimal(3.0);
BigDecimal e =c.divide(d);
System.out.println("e = " + e);
复制代码
プログラムを実行すると、次のエラーが報告されます。
これは、除算中に商が無限小数 (0.333...) であり、演算の結果が正確な数であると予想される場合、ArithmeticException がスローされるためです。小数点以下は必ず入れてください。上記のコードは、次のように変更できます。
BigDecimal c = new BigDecimal(1.0);
BigDecimal d =new BigDecimal(3.0);
BigDecimal e =c.divide(d,2,RoundingMode.HALF_UP);
System.out.println("e = " + e);
复制代码
値渡しと参照渡し
説明の例:
public static void func(int a)
{
a=30;
}
public static void main(String[] args)
{
int a=20;
func(a);
System.out.println(a);
String c="abc";
Strinfunc(c);
System.out.println(c);
}
public static void Strinfunc(String c)
{
c="c";
}
复制代码
説明: 値渡しと参照渡しを理解する必要がある場合は、上記の例に対する答えを簡単に出力できます。
值传递:是指在调用函数时,将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,就不会影响到实际参数。
引用传递:是指在调用函数时,将实际参数的地址传递到函数中,那么在函数中对参数进行修改,将会影响到实际参数。
List.subList内存泄露
实例说明:
public static void main(String[] args)
{
List cache = new ArrayList();
try
{
while (true)
{
List list = new ArrayList();
for (int j = 0; j < 100000; j++)
{
list.add(j);
}
List sublist = list.subList(0, 1);
cache.add(sublist);
}
}
finally
{
System.out.println("cache size = " + cache.size());
}
}
复制代码
说明:这是因为SubList的实例中,保存有原有list对象的强引用,只要sublist没有被jvm回收,那么这个原有list对象就不能gc,即使这个list和其包含的对象已经没有其他任何引用。
for循环中删除元素报错
示例:
public static void main(String[] args) {
String test = "1,2,3,4,5,6,7";
List<String> testList = Arrays.asList(test.split(","));
for(int i = 0; i < testList.size(); i++){
String temp = testList.get(i);
testList.remove(temp);
}
}
复制代码
运行上述的代码报如下错误:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(AbstractList.java:161)
at java.util.AbstractList$Itr.remove(AbstractList.java:374)
at java.util.AbstractCollection.remove(AbstractCollection.java:293)
at com.skywares.fw.juc.integer.ListTest.main(ListTest.java:14)
复制代码
这是因为fail-fast,即快速失败,它是Java集合的一种错误检测机制。当多个线程对集合(非fail-safe的集合类)进行结构上的改变的操作时,有可能会产生fail-fast机制,这个时候就会抛出ConcurrentModificationException(当方法检测到对象的并发修改,但不允许这种修改时就抛出该异常)。
解决的办法
可以通过迭代器来进行删除、或者采用java8的filter过滤 采用java8的filter来过滤
testList.stream().filter(t -> !t.equals("a")).collect(Collectors.toList());
System.out.println(testList);
复制代码
SimpleDateformat格式化时间
SimpleDateFormat是大家比较常用的,而且在一般情况下,一个应用中模式都是一样的,所以很多人都喜欢使用如下的方式定义SimpleDateFormat
public class SimpleDateFormatTest
{
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
public static void main(String[] args)
{
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
System.out.println(simpleDateFormat.format(Calendar.getInstance()
.getTime()));
}
}
复制代码
采用这样的定义方式,如果在多线程的情况下,存在很大的安全隐患。
public class SimpleDateFormatTest
{
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1,
TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000));
public static void main(String[] args)
{
SimpleDateFormatTest simpleDateFormatTest =new SimpleDateFormatTest();
simpleDateFormatTest.test();
}
public void test()
{
while (true)
{
poolExecutor.execute(new Runnable()
{
@Override
public void run()
{
String dateString = simpleDateFormat.format(new Date());
try
{
Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(dateString.equals(dateString2));
}
catch (ParseException e)
{
e.printStackTrace();
}
}
});
}
}
}
复制代码
运行代码如果出现了false,则说明SimpleDateFormat在多线程不安全,导致结果错误,那么我们如何避免呢?可以采用java8提供的DateTimeFormatter来解决SimpleDateFormat在多线程的不安全问题。
未释放对应的资源,导致内存溢出
示例
ZipFile zipFile = new ZipFile(fileName);
Enumeration<ZipEntry> elments = (Enumeration<ZipEntry>) zipFile.getEntries();
while (elments.hasMoreElements())
{
System.out.println(elments.nextElement());
}
复制代码
说明:数据库资源,文件资源,Socket资源以及流资源等,这些资源都是有限的,当程序中的代码申请了资源之后,却没有释放,就会导致资源没有被释放,当系统没有充足的资源时,就会导致系统因为资源枯竭而导致服务不可用。
ThreadLocal内存泄漏
ThreadLocal もインタビューでよく聞かれる質問ですが、使用中にオブジェクトを手動で解放する必要があります。そうしないと、メモリ リークが報告されます。
典型的なメモリ リークの例
static class UserInfoContext{
public static ThreadLocal<UserInfo> userLocal =new ThreadLocal<>();
}
@WebFilter("/*")
public class UserInfoFilter implements Filter{
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
if(hasLogined(req)){
UserContext.userLocal.set(extractUserInfo(req));
}
chain.doFilter(req, res);
}
}
复制代码
説明します:
アプリ アプリケーションが Tomcat によって読み込まれて実行されると、リクエストが UserInfoFilter を通過するときに、実行スレッドのプライベート マップにエントリが挿入され、このエントリは UserInfo オブジェクトへの強力な参照を持ちます。 Tomcat はサーブレット/フィルター オブジェクトへの参照を解放しましたが、スレッドのプライベート ThreadLocalMap にはまだ UserInfo オブジェクトへの参照があり、UserInfo オブジェクトに到達可能であるため、UserInfo クラスにも到達可能です. Tomcat はクラス分離を行っているためです. 、次にアプリをロードしたときに同じクラスがロードされたままになる.ロードし直す.Tomcatでアプリのロードとアンロードを繰り返すと、JVMPermGenのクラスがどんどん蓄積され、最終的にjava.lang.OutOfMemoryErrorが発生する. : PermGen スペース。
要約する
この記事では、いくつかの一般的なピットイン記録を示します. 経験を積んで、ピットインを繰り返さないようにしてください. 質問がある場合は、お気軽にフィードバックをお寄せください.