国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

第12章 元編程與注解、反射 《Kotlin 項目實戰開發》

joyqi / 2247人閱讀

摘要:第章元編程與注解反射反射是在運行時獲取類的函數方法屬性父類接口注解元數據泛型信息等類的內部信息的機制。本章介紹中的注解與反射編程的相關內容。元編程本質上是一種對源代碼本身進行高層次抽象的編碼技術。反射是促進元編程的一種很有價值的語言特性。

第12章 元編程與注解、反射

反射(Reflection)是在運行時獲取類的函數(方法)、屬性、父類、接口、注解元數據、泛型信息等類的內部信息的機制。這些信息我們稱之為 RTTI(Run-Time Type Information,運行時類型信息) 。

注解(Annotation)是我們給代碼添加的元數據。使用注解可以寫出更加簡潔干凈的代碼,同時還可以在編譯期進行類型檢查。Kotlin 的注解完全兼容 Java 的注解。

本章介紹 Kotlin 中的注解與反射編程的相關內容。

12.1 元編程簡介

說到元編程(Meta-programming),我們從 Meta- 這個前綴開始說起。Meta- 這個前綴在在西方哲學界指的是:關于事物自身的事物。比如,心理學領域有一門專門研究關于人類認知心理的學科叫認知心理學(cognitive psychology)。而還有一門學科是研究人對自己的認知過程的認知,叫做元認知心理學(Meta cognitive psychology ),又稱反省認知、監控認知、超認知、反審認知等。元認知的本質是人類對自身認知活動的自我意識和自我調節。

再例如, meta-knowledge 就是“關于知識本身的知識”,meta-data 就是“關于數據的數據”,meta-language 就是“關于語言的語言”,而 meta-programming 也就是“關于編程的編程”, 也就是我們通常所說的“元編程”。

元編程(Meta-programming)是指用代碼在編譯期或運行期生成或改變代碼的一種編程形式。編寫元程序的語言稱之為元語言,被操縱的語言稱之為目標語言。如果一門語言中具備同時是元語言也是目標語言的能力,這就是反射。

一般代碼的操作對象是數據,元編程操作的對象是其他代碼。無關業務邏輯,只跟當前代碼結構相關的代碼。比如在Java中在運行時通過反射把所有以*ServiceImpl 結尾的類找出來,加上log日志或者進行監控統計等其它動作。

除非程序的運行期的輸入數據會被直接或間接轉化成代碼,否則元編程不會給程序帶來新的邏輯。元編程本質上是一種對源代碼本身進行高層次抽象的編碼技術。"元編程"比"我們手寫代碼"多提供了一個抽象層次! 我們其實就是用代碼中的元數據(按照一定的協議規則來定義,也就是注解的語法規范)來進行動態插入新代碼邏輯,也就是用來動態生成代碼的程序。其實,根本沒有什么“元編程”,有的只是“編程”。

反射是促進元編程的一種很有價值的語言特性。編程的語言中的泛型支持也使用元編程能力。元編程通常有兩種方式:一種是通過應用程序接口(API)來暴露運行時系統的內部信息;另一種方法是在運行時動態執行包含編程命令的字符串。因此,“程序能編寫程序”。雖然兩種方法都能用,但大多數方法主要靠其中一種。

注解是把編程中的元數據信息直接寫在源代碼中,而不是保存在外部文件中。

在使用注解之前(甚至在使用之后),XML配置文件被廣泛的應用于編程過程中的元數據的描述。后來程序員們逐漸發現XML的維護越來越糟糕了,進而希望直接使用一些和代碼緊耦合的“元數據”,而不是像 XML 那樣和代碼分離。把注解使用的淋漓盡致的 Spring Boot 框架中,基本不需要一行XML配置,幾乎全部使用注解就搞定一個 Spring 企業級應用的開發。

“XML vs. Annotation”,這其實是一個 “陰陽交融” 的編程之道,很多時候要看具體的問題場景來決定采用哪種方式。XML配置就是為了分離代碼和配置而引入的,而注解是為了希望使用一些和代碼緊耦合的東西。萬事萬物就是這樣的陰陽交合辯證發展的過程。

注解是將元數據附加到代碼的方法。而反射可以在運行時把代碼中的注解元數據獲取到,并在目標代碼執行之前進行動態代理,實現業務邏輯的動態注入,這其實就是 AOP (Aspect Oriented Programming,面向切面編程(也叫面向方面)的核心思想——通過運行期動態代理(和預編譯方式)實現在不修改源代碼的情況下, 給程序動態添加新功能的一種技術。

例如,在 Spring 、 Mybatis 、JPA 等諸多框架中的核心功能都是使用了注解與反射的技術來實現的。例如我們常用的 Spring 框架中的各種注解 @Repository 、@Service 、 @Transactional 、@RequestMapping 、@ResponseBody 等),Mybatis 框架中的各種注解 @Select 、 @Update 、@Param 等。

另外,需要重點提到的就是當下非常流行的 Spring Boot 框架。在使用 Spring Boot 開發企業級應用時,完全不需要使用一行 XML 配置,整個的源代碼工程都能基于注解來開發(application.propertis配置文件另當別論,更多關于SpringBoot框架開發的知識,我們將在后面的章節中介紹)。

12.2 注解

Kotlin的注解跟Java注解也完全兼容。我們可以在Kotlin代碼中很自然地使用Java中的注解。也就是說,我們使用Kotlin語言集成SpringBoot框架開發的過程將會非常自然,幾乎跟使用原生Java語言開發一樣流暢,同時還能享受Kotlin語言帶來的諸多簡潔優雅同時還非常強大的特性。

12.2.1 聲明注解

Kotlin中聲明注解使用 annotation class 關鍵字。例如,我們聲明兩個注解Run和TestCase 如下

@Target(AnnotationTarget.CLASS,
        AnnotationTarget.FUNCTION,
        AnnotationTarget.VALUE_PARAMETER,
        AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class TestCase(val id: String)


@Target(AnnotationTarget.CLASS,
        AnnotationTarget.FUNCTION,
        AnnotationTarget.VALUE_PARAMETER,
        AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class Run

從這個關鍵字上我們就可以看出注解也是一種 class ,編譯器同樣可以對注解類型在編譯期進行類型檢查。

我們自定義的注解上面使用的注解,我們稱之為元注解(meta-annotation)。我們通過向注解類添加元注解的方法來指定其他屬性。元注解說明如下表

元注解名稱 功能說明
@Target 指定這個注解可被用于哪些元素 ( 這些元素定義在kotlin.annotation.AnnotationTarget 枚舉類中。它們是:類 CLASS, 注解類 ANNOTATION_CLASS,泛型參數 TYPE_PARAMETER,函數 FUNCTION, 屬性 PROPERTY, 用于描述域成員變量的 FIELD,局部變量 LOCAL_VARIABLE,VALUE_PARAMETER,CONSTRUCTOR,PROPERTY_GETTER,PROPERTY_SETTER, 用于描述類、接口(包括注解類型) 或enum聲明的 TYPE, 表達式 EXPRESSION,文件 FILE,類型別名TYPEALIAS等。
@Retention 指定這個注解的信息是否被保存到編譯后的 class 文件中, 以及在運行時是否可以通過反射訪問到它, 可取的枚舉值有3個,分別是: SOURCE (注解數據不存儲在二進制輸出),BINARY(注解數據存儲在二進制輸出中, 但反射不可見), RUNTIME(注解數據存儲在二進制輸出中, 可用于反射 (默認值 ) 。
@Repeatable 允許在單個元素上多次使用同一個注解
@MustBeDocumented 表示這個注解是公開 API 的一部分, 在自動產生的 API 文檔的類或者函數簽名中, 應該包含這個注解的信息。
12.2.2 使用注解

上面我們聲明了Run注解,它可以使用在CLASS,FUNCTION,VALUE_PARAMETER,EXPRESSION上。我們這里給出的示例是用在類上

@Run
class SwordTest {}

我們聲明的 TestCase 注解,它有個構造函數,傳入的參數是一個String類型的id。我們把這個注解用在函數上

@Run
class SwordTest {
    @TestCase(id = "1")
    fun testCase(testId: String) {
        println("Run SwordTest ID = ${testId}")
    }
}

上面是注解在代碼中的簡單使用。其中的 @TestCase(id = "1") 是注解的構造函數的使用。注解可以有帶參數的構造器。注解參數的可支持數據類型如下:

所有基本數據類型(Int,Float,Boolean,Byte,Double,Char,Long,Short)

String 類型

KClass 類型

enum 類型

Annotation 類型

以上所有引用類型的數組(注意,不包括基本數據類型)

例如下面的都是合法的注解構造函數的參數類型

annotation class TestCase(val id: String)
annotation class TestCasee(val id: Int)
annotation class TestCaseee(val id: Array)
annotation class TestCaseeee(val id: Run)
annotation class TestCaseeeeee(val id: KClass)

而下面的兩種聲明編譯不通過

annotation class TestCaseeeee(val id: Array) 
annotation class TestCaseeeeee(val id: SwordTest)

另外,需要注意的是:注解類型不能有 null 類型,因為JVM不支持將null作為注解屬性的值存儲。如果注解用作另一個注解的參數,其名稱不以@字符為前綴

annotation class AnnoX(val value: String)

annotation class AnnoY(
        val message: String,
        val annoX: AnnoX = AnnoX("X"))

Java注解與Kotlin完全兼容。下面是一個Kotlin使用JUnit 4進行單元測試代碼編寫的例子

package com.easy.kotlin

import com.easy.kotlin.annotation.SwordTest
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

@RunWith(JUnit4::class)
class AnnotationClassNoteTest {
    @Test
    fun testAnno() {
        val sword = SwordTest()
        sword.testCase("10000")
    }
}

我們可以看出,除了@RunWith(JUnit4::class) 這地方的反射寫法稍微有點不同外,剩下的跟我們在Java中使用 JUnit 的注解的方式基本上是一樣的。

12.2.3 處理注解

定義了注解,并在需要的時候給相關類,類屬性加上注解信息,如果沒有相應的注解信息處理邏輯流程,那么注解可以說是廢掉了,沒啥實用價值。如何讓注解在程序運行的時候發揮其特有的作用呢?核心就在于注解處理的代碼了。本小節我們將學習到怎樣進行注解信息的獲取和處理。因為注解信息的獲取主要是使用反射API,所以我們也會在本節中講到反射相關的內容。

首先,我們的目標測試類是

@Run
class SwordTest {

    @TestCase(id = "1")
    fun testCase(testId: String) {
        println("Run SwordTest ID = ${testId}")
    }

}

這里我們主要介紹 @TestCase 注解作用在函數上的處理過程。

::class 引用

首先,我們聲明一個變量指向 SwordTest 對象實例

 val sword = SwordTest()

然后,我們就可以通過這個變量來獲取此對象的類的信息。使用 ::class 來獲取sword對象實例的 KClass 類的引用

val kClass = sword::class

上面的這行代碼,Kotlin編譯器會自動推斷出kClass變量的類型是

val kClass:KClass = sword::class

這個KClass 數據類型我們將在下面的小節中介紹。

declaredFunctions 擴展屬性

下面,我們需要獲取sword對象類型所聲明的所有函數。Kotlin中可以直接使用擴展屬性 declaredFunctions 來獲取這個類中聲明的所有函數(對應的反射數據類型是 KFunction )。代碼如下

val declaredFunctions = kClass.declaredFunctions

返回的是一個 Collection>> , 其中<> 是Kotlin泛型中的星投影,類似Java中的 通配符。

這個 declaredFunctions 擴展屬性的實現源碼如下

@SinceKotlin("1.1")
val KClass<*>.declaredFunctions: Collection>
    get() = (this as KClassImpl).data().declaredMembers.filterIsInstance>()
annotations 屬性

KFunction 類型繼承了 KCallable , KCallable又繼承了 KAnnotatedElement ,KAnnotatedElement 中有個 public val annotations: List 屬性里面存儲了該函數所有的注解的信息。通過遍歷這個存儲Annotation 的List,我們獲取到 TestCase 注解

for (f in declaredFunctions) {
        // 處理 TestCase 注解,使用其中的元數據
        f.annotations.forEach {
            if (it is TestCase) {
                val id = it.id // TestCase 注解的屬性 id
                doSomething(id) // 注解處理邏輯
            }
        }
}
call 函數

另外,如果我們想通過反射來調用函數,可以直接使用 call 函數

f.call(sword, id) 

上面的代碼等價于 f.javaMethod?.invoke(sword, id) 。

到這里,我們就完成了一個簡單的注解處理器。完整的代碼如下

fun testAnnoProcessing() {
    val sword = SwordTest()
    // val kClasss:KClass = sword::class // 類型聲明可省略
    val kClass = sword::class

    val declaredFunctions = kClass.declaredFunctions // 獲取sword對象類型所聲明的所有函數
    println(declaredFunctions)

    for (f in declaredFunctions) {
        // 處理 TestCase 注解,使用其中的元數據
        f.annotations.forEach {
            if (it is TestCase) {
                val id = it.id
                doSomething(id) // 注解處理邏輯
                f.call(sword, id) // 等價于 f.javaMethod?.invoke(sword, id)
            }
        }
    }
}

private fun doSomething(id: String) {
    println("Do Something in Annotation Processing ${id} ${Date()} ")
}

@Target(AnnotationTarget.CLASS,
        AnnotationTarget.FUNCTION,
        AnnotationTarget.VALUE_PARAMETER,
        AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class TestCase(val id: String)

class SwordTest {

    @TestCase(id = "1")
    fun testCase(testId: String) {
        println("Run SwordTest ID = ${testId}")
    }

}

測試代碼

fun main(args: Array) {
    testAnnoProcessing()
}

輸出

[fun com.easy.kotlin.annotation.SwordTest.testCase(kotlin.String): kotlin.Unit]
Do Something in Annotation Processing 1 Mon Oct 23 23:04:09 CST 2017 
Run SwordTest ID = 1
12.3 反射

在上面小節中的注解信息的獲取與處理邏輯的實現中,其實我們已經用到了反射。反射是指在運行時(Run Time),程序可以訪問、檢測和修改它本身狀態或行為的一種能力。Kotlin中的函數和屬性也是頭等公民,我們可以通過反射來內省屬性和函數:如運行時屬性名或類型,函數名或類型等。

在Kotlin中我們有兩種方式來實現反射的功能。一種是調用Java 的反射包 java.lang.reflect 下面的API ,另外一種方式就是直接調用Kotlin語言提供的kotlin.reflect 包下面的API 。 不過因為反射功能的應用場景并非所有編程場景都用到,所有Kotlin把kotlin.reflect 包的實現放到了多帶帶的kotlin-reflect-1.1.50.jar (當前版本號是1.1.50) 里面。所以在實際工程中,如果我們需要使用Kotlin的反射功能,以Gradle為例,需要在build.gradle配置文件中添加依賴

compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

Kotlin反射API類的層次結構如下圖所示

12.3.1 類引用

為了方便講解,我們先定義一個代碼實例

open class BaseContainer

class Container> : BaseContainer {
    var elements: MutableList

    constructor(elements: MutableList) {
        this.elements = elements
    }

    fun sort(): Container {
        elements.sort()
        return this
    }

    override fun toString(): String {
        return "Container(elements=$elements)"
    }

}

反射是在運行時獲取一個類引用。我們已經知道使用 ::class 調用可以獲取到當前對象的 KClass對象

val container = Container(mutableListOf(1, 3, 2, 5, 4, 7, 6))
val kClass = container::class // 獲取KClass對象

需要注意的是,Kotlin中類引用和Java中類引用是不同的,要獲得java類的引用,可以直接使用 javaClass 這個擴展屬性

val jClass = container.javaClass // 獲取Java Class對象

javaClass 擴展屬性在Kotlin中的實現源碼是

public inline val  T.javaClass : Class
    @Suppress("UsePropertyAccessSyntax")
    get() = (this as java.lang.Object).getClass() as Class

或者使用KClass實例的 .java 屬性

val jkCLass  = kClass.java

這個KClass.java 的擴展屬性的實現源碼如下

@Suppress("UPPER_BOUND_VIOLATED")
public val  KClass.java: Class
    @JvmName("getJavaClass")
    get() = (this as ClassBasedDeclarationContainer).jClass as Class
12.3.2 函數引用

例如,我們有一個簡單的判斷一個Int整數是否是奇數的函數

fun isOdd(x: Int) = x % 2 != 0

我們可以代碼中直接調用

>>> isOdd(7)
true
>>> isOdd(2)
false

另外,在高階函數中我們想把它當做一個參數來使用,可以使用 :: 操作符

val nums = listOf(1, 2, 3)
val filteredNums = nums.filter(::isOdd)
println(filteredNums) // [1, 3]

這里的 ::isOdd 就是一個函數類型 (Int) ->Boolean 的值 。

12.3.3 屬性引用

在Kotlin中,訪問屬性是屬于第一級對象,我們可以使用 :: 操作符

var one = 1
fun testReflectProperty() {
    println(::one.get()) // 1
    ::one.set(2)
    println(one)         // 2
}

fun main(args: Array) {
    testReflectProperty()
}

表達式 ::one 等價于類型為KProperty的一個屬性,它可以允許我們通過 get 函數獲取值 ::one.get() 。

對于可變屬性 var one = 1 ,返回類型為KMutableProperty的值,并且還有set方法 ::one.set(2) 。

12.3.4 綁定函數和屬性引用

我們可以引用一個對象實例的方法。例如下面的代碼

val digitRegex = "d+".toRegex()
digitRegex.matches("7") // true
digitRegex.matches("6") // true
digitRegex.matches("5") // true
digitRegex.matches("X") // false

其中的 digitRegex.matches 重復出現,顯得“樣板化”。 在Kotlin中可以直接引用digitRegex對象實例的matches方法。上面的代碼我們可以寫成下面這樣

val isDigit = digitRegex::matches  // 引用 digitRegex 對象實例的 matches 方法
isDigit("7")// true
isDigit("6")// true
isDigit("5")// true
isDigit("X")// true

是不是很酷? 真的是相當簡潔。

12.4 使用反射獲取泛型信息

在Java中,使用反射的一個代碼實例如下

package com.easy.kotlin;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

interface StudentService {
    List findStudents(String name, Integer age);
}

public class ReflectionDemo {

    public static void main(String[] args) {
        StudentServiceImpl studentService = new StudentServiceImpl();
        studentService.save(new Student("Bob", 20));
        studentService.findStudents("Jack", 20);

        // 反射API調用示例
        final Class studentServiceClass = studentService.getClass();
        Class[] classes = studentServiceClass.getDeclaredClasses();
        Annotation[] annotations = studentServiceClass.getAnnotations();
        ClassLoader classLoader = studentServiceClass.getClassLoader(); // Returns the class loader for the class
        Field[] fields = studentServiceClass.getDeclaredFields(); // 獲取類成員變量
        Method[] methods = studentServiceClass.getDeclaredMethods(); // 獲取類成員方法
        try {
            methods[0].getName(); // save
            methods[0].invoke(studentService, "Jack",20);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}

class StudentServiceImpl extends BaseService implements StudentService {

    public List findStudents(String name, Integer age) {
        return Arrays.asList(new Student[] {new Student("Jack", 20), new Student("Rose", 20)});
    }

    @Override
    public int save(Student student) {
        return 0;
    }
}

abstract class BaseService {
    abstract int save(T t);
}

class Student {

    String name;
    Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

通過反射,我們可以獲取一個類的注解、方法、成員變量、方法等等。那么我們能不能通過反射獲取到泛型的信息呢?我們知道 Java中的泛型采用擦拭法。在運行時,無法得到自己本身的泛型信息。而當這個類繼承了一個父類,父類中有泛型的信息,那么我們可以通過調用getGenericSuperclass()方法得到父類的泛型信息。getGenericSuperclass()是Generic繼承的特例,對于這種情況子類會保存父類的Generic參數類型,返回一個ParameterizedType。另外,我們所說的 Java 泛型在字節碼中會被擦除,并不總是擦除為 Object 類型,而是擦除到上限類型。

在Kotlin也是一樣的泛型機制。所以,通過反射能拿到的也只能是有繼承父類泛型信息的子類泛型。

class A

open class C
class B : C()  // 繼承父類 C()  

fun fooA() {
    // 無法在此處獲得運行時 T 的具體類型!!!運行報錯:java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType

    val parameterizedType = A()::class.java.genericSuperclass as ParameterizedType
    val actualTypeArguments = parameterizedType.actualTypeArguments
    for (type in actualTypeArguments) {
        val typeName = type.typeName
        println("typeName = ${typeName}")
    }
}

fun fooB() {
    // 當繼承了父類 C 的時候,在此處獲得運行時 genericSuperclass T 的具體類型
    val parameterizedType = B()::class.java.genericSuperclass as ParameterizedType
    val actualTypeArguments = parameterizedType.actualTypeArguments
    for (type in actualTypeArguments) {
        val typeName = type.typeName
        println("typeName = ${typeName}") // typeName = java.lang.Integer
    }
}

fun main(args: Array) {
    // fooA() 
    fooB() 
}

下面我們通過一個簡單的實例來說明Kotlin中的反射怎樣獲取泛型代碼的基本信息。

首先,聲明一個父類 BaseContainer

open class BaseContainer

然后,聲明一個 Container> 繼承它

class Container> : BaseContainer {
    var elements: MutableList

    constructor(elements: MutableList) {
        this.elements = elements
    }

    fun sort(): Container {
        elements.sort()
        return this
    }

    override fun toString(): String {
        return "Container(elements=$elements)"
    }
}

聲明一個 Container對象實例

val container = Container(mutableListOf(1, 3, 2, 5, 4, 7, 6))

獲取container 的KClass 對象引用

val kClass = container::class // 獲取KClass對象

KClass對象的 typeParameters 屬性中存有類型參數的信息

val typeParameters = kClass.typeParameters // 獲取類型參數typeParameters信息,也即泛型信息

val kTypeParameter: KTypeParameter = typeParameters[0]
println(kTypeParameter.isReified) // false
println(kTypeParameter.name) // T
println(kTypeParameter.upperBounds) // [kotlin.Comparable]
println(kTypeParameter.variance) // INVARIANT

KClass的 constructors 屬性中存有構造函數的信息,我們可以從中獲取構造函數的入參等信息

val constructors = kClass.constructors
for (KFunction in constructors) {
    KFunction.parameters.forEach {
        val name = it.name
        val type = it.type
        println("name = ${name}") // elements
        println("type = ${type}") // kotlin.collections.MutableList
        for (KTypeProjection in type.arguments) {
            println(KTypeProjection.type) // T
        }
    }
}
本章小結

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/68014.html

相關文章

  • Kotlin + Spring Boot : 下一代 Java 服務端開發

    摘要:下一代服務端開發下一代服務端開發第部門快速開始第章快速開始環境準備,,快速上手實現一個第章企業級服務開發從到語言的缺點發展歷程的缺點為什么是產生的背景解決了哪些問題為什么是的發展歷程容器的配置地獄是什么從到下一代企業級服務開發在移動開發領域 《 Kotlin + Spring Boot : 下一代 Java 服務端開發 》 Kotlin + Spring Boot : 下一代 Java...

    springDevBird 評論0 收藏0
  • Java學習路線總結,搬磚工逆襲Java架構師(全網最強)

    摘要:哪吒社區技能樹打卡打卡貼函數式接口簡介領域優質創作者哪吒公眾號作者架構師奮斗者掃描主頁左側二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領導們的談話,現在公司的現狀是碼農太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區Java技能樹打卡?【打卡貼 day2...

    Scorpion 評論0 收藏0
  • Java開發

    摘要:大多數待遇豐厚的開發職位都要求開發者精通多線程技術并且有豐富的程序開發調試優化經驗,所以線程相關的問題在面試中經常會被提到。將對象編碼為字節流稱之為序列化,反之將字節流重建成對象稱之為反序列化。 JVM 內存溢出實例 - 實戰 JVM(二) 介紹 JVM 內存溢出產生情況分析 Java - 注解詳解 詳細介紹 Java 注解的使用,有利于學習編譯時注解 Java 程序員快速上手 Kot...

    LuDongWei 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<