<ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>


      本篇文章是 Stack Overflow 周報(bào)的第二周,共收集了 4 道高關(guān)注的問(wèn)題和對(duì)應(yīng)的高贊回答。公眾號(hào)「渡碼」為日更,歡迎關(guān)注。

      ?

      DAY1.??serialVersionUID 的重要性??

      關(guān)注: 2820,最高贊: 2152

      這篇文章介紹一下 Java 中 serialVersionUID 屬性的含義以及重要性。從屬性可以看出它與序列化有關(guān)系,所以在?
      java.io.Serializable 接口的注釋中對(duì)它有詳細(xì)的介紹,下面我們對(duì)照文檔注釋來(lái)學(xué)習(xí)一下。Java
      中每個(gè)可序列化的類都有一個(gè)版本號(hào)與之關(guān)聯(lián),這個(gè)版本號(hào)就是 serialVersionUID。它在對(duì)象反序列化時(shí)使用, 用于判斷該類的發(fā)送方和接收方的
      serialVersionUID 是否一致,如果接收方裝載的類的 serialVersionUID 與發(fā)送方不一致,則拋出
      InvalidClassException 異常。一個(gè)可序列化的類可以顯示地聲明 serialVersionUID 屬性,但必須是
      static,final,long 修飾的,如:
      ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
      下面結(jié)合實(shí)際的例子來(lái)看看 serialVersionUID 的用法以及作用,上面說(shuō)了 serialVersionUID
      屬性是定義在可序列化的類中,所以我們的類需要實(shí)現(xiàn) java.io.Serializable 接口。因此,我們定義的 Person 類如下:
      package com.cnblogs.duma.week2; import java.io.Serializable; public class
      Personimplements Serializable { private static final long serialVersionUID = 42L
      ;public int age; public String name; public Person(int age, String name){ this
      .age = age; this.name = name; } @Override public String toString() { return age
      + "," + name; } }
      接下來(lái),我們?cè)诙x兩個(gè)方法分別用來(lái)序列化和反序列化,序列化方法如下:
      // 將 Person 對(duì)象序列化后存到文件 public static void ser() { Person p = new Person(28,
      "duma"); System.out.println("person Seria:" + p); try { FileOutputStream fos =
      new FileOutputStream("person.txt"); ObjectOutputStream oos = new
      ObjectOutputStream(fos); oos.writeObject(p); oos.flush(); oos.close(); }catch
      (IOException e) { e.printStackTrace(); } }
      序列化方法如下:
      // 從文件中反序列化出 Person 對(duì)象 public static void deser() { Person p; try {
      FileInputStream fis= new FileInputStream("person.txt"); ObjectInputStream ois =
      new ObjectInputStream(fis); p = (Person) ois.readObject(); ois.close();
      System.out.println(p.toString()); }catch (IOException | ClassNotFoundException
      e) { e.printStackTrace(); } }
      先調(diào)用 ser 方法,成功后可以看到項(xiàng)目根目錄下生成了 person.txt 文件,然后在調(diào)用 deser 方法可以成功反序列化 Person
      對(duì)象并輸出結(jié)果。這波操作就是正常的序列化和反序列化操作?;氐浇裉斓闹黝},為了驗(yàn)證 serialVersionUID 屬性的作用,我們可以在調(diào)用完 ser
      方法后,先修改 serialVersionUID 值,然后再調(diào)用 deser 方法,這時(shí)就會(huì)拋出 java.io.InvalidClassException
      異常。

      明白了 serialVersionUID 屬性的含義和作用,接下來(lái)我們?cè)賮?lái)看看它的重要性。在我們的例子中我們顯式地定義了 serialVersionUID
      屬性,如果沒(méi)有顯式地指定 serialVersionUID,序列化運(yùn)行時(shí)會(huì)根據(jù)類的信息計(jì)算一個(gè)默認(rèn)值,《Effective
      Java》一書中提到這些信息包括類名、實(shí)現(xiàn)的接口、public和protected的成員。雖然有默認(rèn)值,但 Java 官方文檔強(qiáng)烈建議我們顯式地定義
      serialVersionUID 屬性,因?yàn)槟J(rèn)的 serialVersionUID
      依賴類的信息,而類的信息可能在不同編譯器下會(huì)不同。因此,如果發(fā)送方和接收方使用的編譯器不同,有可能導(dǎo)致默認(rèn)的 serialVersionUID
      不一致從而導(dǎo)致接收方無(wú)法正常反序列化,同時(shí) Java 官方也建議使用 private 修飾 serialVersionUID,這樣可以防止子類繼承這個(gè)屬性。

      對(duì)于上面提到的《Effective Java》一書中的內(nèi)容,我們可以做個(gè)簡(jiǎn)單的驗(yàn)證。因?yàn)樯赡J(rèn)的 serialVersionUID 會(huì)用到 public
      成員信息,那我們改變成員變量就會(huì)導(dǎo)致 serialVersionUID 值改變。首先我們將 Person 類中的 serialVersionUID
      屬性刪掉,調(diào)用 ser 方法序列化。然后在 Person 中加一個(gè)成員,比如:public String nickname = "zhangsan";
      ,然后調(diào)用 deser 方法,可以看到程序拋出 java.io.InvalidClassException 異常。這同時(shí)也警示我們盡量顯示地定義
      serialVersionUID 屬性。

      查看原文
      <https://mp.weixin.qq.com/s?__biz=MjM5MjcwMjk4OA==&mid=2247483838&idx=1&sn=38a0fae303709ff41d9179d3989b5321&chksm=a6a376dd91d4ffcb34a5ec0ace9e0035079a4b356df99a66dc9592e2b8b136f59a4dde0df66f&token=428338994&lang=zh_CN#rd>

      ?

      DAY2.??創(chuàng)建線程到底用哪種方式

      關(guān)注: 1972,最高贊: 1583

      我們知道 Java 實(shí)現(xiàn)線程的方式有兩種, 一種是繼承 Thread 類,另一種是實(shí)現(xiàn) Runnable
      接口。那么問(wèn)題來(lái)了,這兩種方式有什么區(qū)別呢?我們應(yīng)該用哪種方式更好呢?下面先簡(jiǎn)單看下這兩種方式的代碼。

      1. 繼承 Thread 類
      static class MyThread extends Thread { @Override public void run() { super
      .run(); } }
      2. 實(shí)現(xiàn) Runnable 接口
      static class Workder implements Runnable { @Override public void run() { } }
      對(duì)于這兩種方式的選擇,Stack Overflow 的回答者普遍認(rèn)為優(yōu)先選擇實(shí)現(xiàn) Runnable 接口的方式,理由如下:

      *
      在面向?qū)ο笾?,繼承意味著添加新功能、修改或者改進(jìn)父類的行為,如果我們沒(méi)有這方面的改動(dòng),那就盡量避免使用繼承,因?yàn)槲覀兊拇a只是單純需要執(zhí)行一些任務(wù),而不需要改造
      Thread 的行為,所以繼承 Runnable 接口更合理,所以上面代碼中繼承的方式類名是 Thread1 是一種(is a)Thread,而實(shí)現(xiàn)
      Runnable 接口的類名是 Worker,一個(gè) Worker 對(duì)象(工人)的“工作”邏輯可以放在 run 方法中,然而這并不意味這這個(gè)工人 7*24
      小時(shí)一直工作。
      * 由于 Java 中不支持多繼承,因此繼承 Thread 類意味著無(wú)法在繼承其他的類,影響代碼的擴(kuò)展性。
      * 繼承 Thread 意味著每個(gè)線程都有一個(gè)唯一的 Thread 的對(duì)象與之對(duì)應(yīng),而實(shí)現(xiàn) Runnable 接口可以讓多個(gè)線程共享同一個(gè)對(duì)象。
      總之,如果我們的類定位在單純地執(zhí)行任務(wù),并不需要改造 Thread 類,那我們就應(yīng)該實(shí)現(xiàn) Runnable 接口。反之,如果我們需要改造 Thread
      類,或者它是一種(is a)線程,那我們就繼承 Thread 類。我目前正在寫的一本關(guān)于 RPC 的書中,創(chuàng)建線程就是以繼承 Thread 為主。

      查看原文
      <https://mp.weixin.qq.com/s?__biz=MjM5MjcwMjk4OA==&mid=2247483849&idx=1&sn=0a7715e39b23e82958161eabaf25d913&chksm=a6a376aa91d4ffbce1c04b4032840c35b5c0e09b503b5ea1b84e9db65b4c71e1610001c6ff9f&token=428338994&lang=zh_CN#rd>

      ?

      DAY3.?反射非用不可嗎

      關(guān)注: 1960,最高贊: 1611

      相信有 Java
      基礎(chǔ)的朋友都知道反射的概念。然而如果你僅僅了解概念,而在工程實(shí)踐中沒(méi)有應(yīng)用的話,那可能總是感覺(jué)有層窗戶紙模模糊糊的。我之前學(xué)習(xí)反射就有這種感覺(jué)。那么今天這篇文章我就完整地梳理一下反射的概念和作用,結(jié)合
      Hadoop RPC 框架聊聊反射為什么非用不可或者說(shuō)用了反射是不是給程序帶來(lái)了非常大的便利性。


      首先,我們看定義:反射是語(yǔ)言提供了一種在運(yùn)行時(shí)檢查和動(dòng)態(tài)調(diào)用類、方法和屬性的能力?;谶@個(gè)能力,反射一般大量應(yīng)用在框架中,如:Spring,Hadoop。從反射的定義我們可能會(huì)問(wèn)一個(gè)問(wèn)題,為什么要在運(yùn)行時(shí)動(dòng)態(tài)地調(diào)用?既然
      Java 是靜態(tài)語(yǔ)言,任何需要調(diào)用的東西為什么不在編譯時(shí)就確定好呢?這個(gè)問(wèn)題也就是在問(wèn)反射的作用是什么以及是不是非用不可。


      我們可以簡(jiǎn)單猜想一下,以類的反射為例,當(dāng)我們使用第三方框架時(shí),框架并不知道用戶定義了什么類,因此框架想要使用用戶的類,只能在運(yùn)行時(shí)動(dòng)態(tài)地檢查類是否存在,再進(jìn)行調(diào)用。下面以
      Hadoop 的 MapReduce 框架為例,看一下它使用反射的一個(gè)例子。

      用戶自定義的類如下:
      // 需要繼承 Mapper 基類,Hadoop 框架才能正常使用它 public class WordCountMapper extends
      Mapper<Object, Text, Text, IntWritable> { // Hadoop 框架會(huì)在創(chuàng)建 WordCountMapper
      對(duì)象后調(diào)用 map 方法 @Override protected void map(Object key, Text value, Context
      context)throws IOException, InterruptedException { // 數(shù)據(jù)處理邏輯 } }
      Hadoop 框架通過(guò)反射調(diào)用 WordCountMapper 的代碼如下:
      // taskContext.getMapperClass() 為運(yùn)行時(shí)用戶傳入的類,WordCountMapper
      org.apache.hadoop.mapreduce.Mapper<INKEY,INVALUE,OUTKEY,OUTVALUE> mapper =
      (org.apache.hadoop.mapreduce.Mapper<INKEY,INVALUE,OUTKEY,OUTVALUE>)
      ReflectionUtils.newInstance(taskContext.getMapperClass(), job);
      // run 方法中循環(huán)調(diào)用 map 方法處理數(shù)據(jù) mapper.run(mapperContext);
      上面是 Hadoop 代碼的一部分,它將類的反射代碼封裝在 ReflectionUtils.newInstance
      方法中,該方法用類的默認(rèn)構(gòu)造方法動(dòng)態(tài)地創(chuàng)建一個(gè)對(duì)象。上述代碼中該方法創(chuàng)建的對(duì)象被強(qiáng)轉(zhuǎn)為 Mapper 類,這就是為什么因?yàn)槲覀兊?
      WordCounterMapper 類要繼承 Mapper。

      因此,可以看到 Hadoop 框架在編譯的時(shí)候并不知道用戶定義了WordCountMapper
      類,只能在運(yùn)行時(shí)根據(jù)配置動(dòng)態(tài)地檢查、調(diào)用。當(dāng)然為了框架能夠正常使用我們定義的類,就需要定義類時(shí)符合框架定義的規(guī)范,在我們的例子中需要遵循的規(guī)范是實(shí)現(xiàn)一個(gè)
      Mapper 基類,并且需要有默認(rèn)構(gòu)造函數(shù)。如果我們?cè)诖a中修改 WordCountMapper 的構(gòu)造函數(shù),那就不符合框架的規(guī)范,反射就會(huì)報(bào)錯(cuò),如下:
      public class WordCountMapper extends Mapper<Object, Text, Text, IntWritable> {
      // 有參構(gòu)造函數(shù)覆蓋默認(rèn)構(gòu)造函數(shù) public WordCountMapper(int a) { a = 100; } }
      再次運(yùn)行,當(dāng) Hadoop 調(diào)用 ReflectionUtils.newInstance 時(shí)找不默認(rèn)構(gòu)造函數(shù)便會(huì)以下報(bào)錯(cuò):
      Error: java.lang.RuntimeException: java.lang.NoSuchMethodException:
      com.cnblogs.duma.mapreduce.WordCountMapper.<init>() at
      org.apache.hadoop.util.ReflectionUtils.newInstance(ReflectionUtils.java:135) at
      org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:751)
      接下來(lái),再舉一個(gè)動(dòng)態(tài)調(diào)用方法的例子,假設(shè)我們要在運(yùn)行時(shí)才能檢查并調(diào)用某方法,寫法如下:
      Method method = foo.getClass().getMethod("doSomething", null);
      method.invoke(foo,null);
      這種場(chǎng)景也是框架比較喜歡用的,比如:Java 的單元測(cè)試框架 Junit4,通過(guò)反射檢查類中帶有 @Test 注解的方法,然后調(diào)用他們運(yùn)行單元測(cè)試。


      從上面兩個(gè)例子可以看到為什么框架對(duì)反射如此鐘情。框架不需要關(guān)心用戶定義了什么類,只要用戶的代碼符合框架定義的規(guī)范,框架就會(huì)在運(yùn)行時(shí)進(jìn)行檢查,并按照自己定義的規(guī)范調(diào)用代碼即可。因此反射可以讓框架和用戶的應(yīng)用解耦,使得開(kāi)發(fā)更方便。

      查看原文
      <https://mp.weixin.qq.com/s?__biz=MjM5MjcwMjk4OA==&mid=2247483859&idx=1&sn=919880091ed358bc6104e0da58a84a01&chksm=a6a376b091d4ffa6169a110dc341bbecf7d0dcaf78ce7db65c28e2fe961d466101dc547e9a21&token=428338994&lang=zh_CN#rd>

      ?

      DAY4.?一行代碼搞定數(shù)組的初始化、搜索、打印

      我們平時(shí)遇到的好多問(wèn)題可能一行代碼就搞定了。平時(shí)遇到問(wèn)題可以多想想是不是已經(jīng)有工具已經(jīng)實(shí)現(xiàn)了,
      如果有的話可以直接拿來(lái)用,避免重復(fù)造輪子。這篇文章今天發(fā)在公眾號(hào)上,算是關(guān)注公眾號(hào)讀者的一個(gè)福利吧。后續(xù)再發(fā)博客。

      以上便是 Stack Overflow 的第二周周報(bào),希望對(duì)你有用,后續(xù)會(huì)繼續(xù)更新,如果想看日更內(nèi)容歡迎關(guān)注公眾號(hào)。

      ?

      公眾號(hào)「渡碼」,分享更多高質(zhì)量?jī)?nèi)容


      友情鏈接
      ioDraw流程圖
      API參考文檔
      OK工具箱
      云服務(wù)器優(yōu)惠
      阿里云優(yōu)惠券
      騰訊云優(yōu)惠券
      京東云優(yōu)惠券
      站點(diǎn)信息
      問(wèn)題反饋
      郵箱:[email protected]
      QQ群:637538335
      關(guān)注微信

        <ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>
          久久精精品久久久久噜噜 | 911在线无码精品秘 入口楼风 | 婷婷激情四射 | 第一页在线 | 毛片啪啪啪 | 欧美二级片 | 国产人妻绿帽3p国语对白 | 国产欧美激情 | 手机A V在线 | 乱伦激情 |