Java static 與 final關(guān)鍵字實(shí)例詳解
深入理解 Java 中 static 和 final 關(guān)鍵字的本質(zhì),掌握類(lèi)加載過(guò)程中的內(nèi)存變化,以及在多線(xiàn)程環(huán)境下的線(xiàn)程安全問(wèn)題。
核心概念速覽
static:屬于類(lèi)而非實(shí)例
本質(zhì):聲明為 static 的成員(變量、方法、代碼塊、內(nèi)部類(lèi))屬于類(lèi)本身,而非任何對(duì)象實(shí)例。
內(nèi)存分配:在類(lèi)加載時(shí),static 成員分配在方法區(qū)(JDK 8+ 為元空間+堆的靜態(tài)存儲(chǔ)區(qū)),只有一份副本,所有實(shí)例共享。
final:不可變
本質(zhì):聲明為 final 的內(nèi)容一旦初始化就不能改變。
適用范圍:
- 類(lèi):不能被繼承(如
String) - 方法:不能被子類(lèi)重寫(xiě)
- 變量:引用不可變(基本類(lèi)型值不變,對(duì)象引用地址不變)
static final:全局常量
本質(zhì):既是類(lèi)級(jí)別的,又是不可變的。通常用于定義編譯期常量。
內(nèi)存優(yōu)化:編譯時(shí)常量會(huì)被編譯器內(nèi)聯(lián)到使用處,不會(huì)觸發(fā)類(lèi)加載。
static 詳解
四種使用形式
1. 靜態(tài)變量(類(lèi)變量)
public class Counter {
// 靜態(tài)變量 - 所有實(shí)例共享
private static int count = 0;
public Counter() {
count++; // 每創(chuàng)建一個(gè)實(shí)例,count 加 1
}
public static int getCount() {
return count;
}
}2. 靜態(tài)常量
public class MathConstants {
// 公開(kāi)靜態(tài)常量
public static final double PI = 3.141592653589793;
public static final double E = 2.718281828459045;
// 私有靜態(tài)常量
private static final int MAX_PRECISION = 100;
}3. 靜態(tài)代碼塊
public class DatabaseConfig {
private static Properties config;
// 靜態(tài)代碼塊 - 類(lèi)加載時(shí)執(zhí)行一次
static {
config = new Properties();
try (InputStream input = DatabaseConfig.class.getResourceAsStream("/config.properties")) {
config.load(input);
} catch (IOException e) {
throw new RuntimeException("加載配置文件失敗", e);
}
}
public static String get(String key) {
return config.getProperty(key);
}
}4. 靜態(tài)方法
public class StringUtils {
// 工具類(lèi)方法,無(wú)需創(chuàng)建對(duì)象
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
public static String capitalize(String str) {
if (isEmpty(str)) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}5. 靜態(tài)內(nèi)部類(lèi)
public class Outer {
private static int outerStatic = 10;
private int outerInstance = 20;
// 靜態(tài)內(nèi)部類(lèi)
public static class StaticInner {
private int innerVar = 30;
public void show() {
// 可以訪(fǎng)問(wèn)外部類(lèi)的靜態(tài)成員
System.out.println("outerStatic = " + outerStatic);
// 不能訪(fǎng)問(wèn)外部類(lèi)的實(shí)例成員
// System.out.println(outerInstance); // 編譯錯(cuò)誤
}
}
public static void main(String[] args) {
Outer.StaticInner inner = new Outer.StaticInner();
inner.show();
}
}static 的內(nèi)存特點(diǎn)
public class StaticMemoryDemo {
static int a = 10; // 存儲(chǔ)在方法區(qū)
int b = 20; // 存儲(chǔ)在堆中
public static void main(String[] args) {
StaticMemoryDemo obj1 = new StaticMemoryDemo();
StaticMemoryDemo obj2 = new StaticMemoryDemo();
// 修改靜態(tài)變量
StaticMemoryDemo.a = 100;
obj1.b = 200;
obj2.b = 300;
System.out.println(obj1.a + ", " + obj1.b); // 100, 200
System.out.println(obj2.a + ", " + obj2.b); // 100, 300
// 修改實(shí)例變量不影響靜態(tài)變量
obj1.a = 1000; // 等價(jià)于 StaticMemoryDemo.a = 1000
System.out.println(StaticMemoryDemo.a); // 1000
}
}final 詳解
三種使用形式
1. final 類(lèi)
// final 類(lèi)不能被繼承
public final class String {
// String 類(lèi)的實(shí)現(xiàn)
}
// 編譯錯(cuò)誤
// public class MyString extends String {}2. final 方法
public class Parent {
// final 方法不能被子類(lèi)重寫(xiě)
public final void essentialMethod() {
System.out.println("重要方法,不允許修改行為");
}
public void normalMethod() {
System.out.println("普通方法,可以重寫(xiě)");
}
}
public class Child extends Parent {
@Override
public void normalMethod() {
System.out.println("重寫(xiě)普通方法");
}
// 編譯錯(cuò)誤
// @Override
// public void essentialMethod() {}
}3. final 變量
public class FinalVariableDemo {
// final 實(shí)例變量 - 必須初始化
private final int x; // 方式1:構(gòu)造器初始化
private final int y = 20; // 方式2:聲明時(shí)初始化
// final 靜態(tài)變量
private static final int STATIC_FINAL = 30;
// final 實(shí)例變量 - 方式3:實(shí)例代碼塊初始化
private final int z;
{
z = 40;
}
public FinalVariableDemo(int x) {
this.x = x;
}
// final 參數(shù) - 參數(shù)不可修改
public void process(final int data, final String str) {
// data = 10; // 編譯錯(cuò)誤
// str = "new"; // 編譯錯(cuò)誤
System.out.println(data + ", " + str);
}
// final 局部變量
public void localFinal() {
final int local = 100;
// local = 200; // 編譯錯(cuò)誤
}
}final 的內(nèi)存安全特性
public class FinalObjectDemo {
public static void main(String[] args) {
// final 對(duì)象 - 引用不可變,但內(nèi)容可變
final List<String> list = new ArrayList<>();
list.add("A"); // ? 合法:對(duì)象內(nèi)容可變
list.add("B");
list.remove(0);
// list = new ArrayList<>(); // ? 非法:引用不可重新賦值
// final 數(shù)組同理
final int[] arr = {1, 2, 3};
arr[0] = 100; // ? 合法:數(shù)組元素可變
// arr = new int[5]; // ? 非法:引用不可變
}
}static final 組合使用
常量定義的最佳實(shí)踐
/**
* 全局常量定義
*/
public final class GlobalConstants {
// 私有構(gòu)造器,防止實(shí)例化
private GlobalConstants() {}
// 編譯期常量 - 編譯器會(huì)內(nèi)聯(lián)
public static final int MAX_CONNECTIONS = 100;
public static final long TIMEOUT_MS = 5000L;
// 運(yùn)行時(shí)常量 - 需要類(lèi)加載
public static final String APP_VERSION = "1.0.0";
public static final String JAVA_VERSION = System.getProperty("java.version");
// 不可變集合
public static final List<String> SUPPORTED_FORMATS =
Collections.unmodifiableList(Arrays.asList("JSON", "XML", "YAML"));
// 不可變 Map
public static final Map<String, Integer> ERROR_CODES;
static {
Map<String, Integer> codes = new HashMap<>();
codes.put("NOT_FOUND", 404);
codes.put("SERVER_ERROR", 500);
ERROR_CODES = Collections.unmodifiableMap(codes);
}
}常量在內(nèi)存中的優(yōu)化
public class ConstantOptimization {
// 編譯期常量 - 會(huì)被內(nèi)聯(lián)
public static final int MAX_SIZE = 100;
// 運(yùn)行時(shí)常量 - 不會(huì)內(nèi)聯(lián)
public static final String CONFIG_PATH = System.getProperty("config.path");
public static void main(String[] args) {
// 使用編譯期常量時(shí),編譯器會(huì)直接替換為 100
// 不會(huì)觸發(fā) ConstantOptimization 類(lèi)的加載
int size = ConstantOptimization.MAX_SIZE;
// 使用運(yùn)行時(shí)常量時(shí),會(huì)觸發(fā)類(lèi)加載
String path = ConstantOptimization.CONFIG_PATH;
}
}類(lèi)加載過(guò)程中的內(nèi)存變化
類(lèi)加載的生命周期
加載 → 鏈接(驗(yàn)證→準(zhǔn)備→解析) → 初始化 → 使用 → 卸載
詳細(xì)過(guò)程解析
階段 1:加載(Loading)
JVM 讀取類(lèi)的字節(jié)碼文件(.class),在內(nèi)存中生成一個(gè)代表這個(gè)類(lèi)的 java.lang.Class 對(duì)象。
此時(shí)內(nèi)存中:
- 方法區(qū)開(kāi)始準(zhǔn)備存儲(chǔ)類(lèi)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
static final的編譯期常量(如MAX_SIZE = 100)不會(huì)觸發(fā)類(lèi)加載,因?yàn)橐驯痪幾g器內(nèi)聯(lián)
階段 2:鏈接(Linking)
2.1 驗(yàn)證(Verification)
確保加載的類(lèi)信息符合 JVM 規(guī)范,沒(méi)有安全風(fēng)險(xiǎn)。
2.2 準(zhǔn)備(Preparation)- 關(guān)鍵階段
為類(lèi)的靜態(tài)變量分配內(nèi)存,并設(shè)置默認(rèn)初始值。
public class PreparationDemo {
// 準(zhǔn)備階段后的狀態(tài):
static int a = 10; // a = 0(零值)
static boolean b = true; // b = false(零值)
static char c = 'A'; // c = '\u0000'(零值)
static long d = 100L; // d = 0L(零值)
// 引用類(lèi)型:null
static String e = "hello"; // e = null(零值)
// static final 編譯期常量:直接設(shè)置為最終值
static final int f = 20; // f = 20(最終值)
// static final 運(yùn)行時(shí)常量:先設(shè)為零值
static final Random rand = new Random();
static final int g = rand.nextInt(100); // g = 0(零值)
}內(nèi)存分配:
static變量在方法區(qū)分配內(nèi)存,設(shè)置零值(0, null, false)static final的編譯期常量(如f = 20)直接設(shè)置為最終值static final的運(yùn)行時(shí)常量(如g)先設(shè)為零值,初始化階段才賦真值
2.3 解析(Resolution)
將常量池中的符號(hào)引用替換為直接引用。
階段 3:初始化(Initialization)- 真正賦值
執(zhí)行 <clinit> 方法(類(lèi)構(gòu)造器),按代碼順序初始化靜態(tài)變量和執(zhí)行靜態(tài)代碼塊。
public class InitializationDemo {
static int a = 10; // 第1步:a = 10
static {
System.out.println("靜態(tài)代碼塊1: a = " + a); // 第2步:輸出 a = 10
a = 20; // 第3步:a = 20
}
static int b = 30; // 第4步:b = 30
static {
System.out.println("靜態(tài)代碼塊2: a = " + a + ", b = " + b);
// 第5步:輸出 a = 20, b = 30
}
public static void main(String[] args) {
System.out.println("最終值: a = " + a + ", b = " + b);
// 第6步:輸出 最終值: a = 20, b = 30
}
}輸出結(jié)果:
靜態(tài)代碼塊1: a = 10
靜態(tài)代碼塊2: a = 20, b = 30
最終值: a = 20, b = 30
階段 4:使用(Using)
public class UsingDemo {
public static void main(String[] args) {
// 主動(dòng)引用 - 會(huì)觸發(fā)類(lèi)初始化
int count = Counter.getCount(); // 觸發(fā) Counter 類(lèi)加載
// 被動(dòng)引用 - 不會(huì)觸發(fā)類(lèi)初始化
int[] arr = new int[10]; // 不會(huì)觸發(fā)數(shù)組類(lèi)型的父類(lèi)初始化
// 訪(fǎng)問(wèn)編譯期常量 - 不觸發(fā)類(lèi)初始化
int max = Constants.MAX_SIZE; // 不觸發(fā) Constants 類(lèi)加載
// 訪(fǎng)問(wèn)運(yùn)行時(shí)常量 - 觸發(fā)類(lèi)初始化
String ver = Constants.VERSION; // 觸發(fā) Constants 類(lèi)加載
}
}線(xiàn)程安全問(wèn)題
static 變量的線(xiàn)程安全問(wèn)題
問(wèn)題示例
public class UnsafeCounter {
// 非線(xiàn)程安全的計(jì)數(shù)器
private static int count = 0;
public static void increment() {
count++; // 非原子操作,存在競(jìng)態(tài)條件
}
public static int getCount() {
return count;
}
}count++ 的實(shí)際操作:
- 讀取 count 的值
- 將 count 加 1
- 將新值寫(xiě)回 count
在多線(xiàn)程環(huán)境下,這三個(gè)步驟可能被交叉執(zhí)行,導(dǎo)致數(shù)據(jù)不一致。
解決方案 1:使用 synchronized
public class SafeCounterWithSync {
private static int count = 0;
// 使用類(lèi)鎖保護(hù)靜態(tài)變量
public synchronized static void increment() {
count++;
}
public synchronized static int getCount() {
return count;
}
}解決方案 2:使用 AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;
public class SafeCounterWithAtomic {
private static final AtomicInteger count = new AtomicInteger(0);
public static void increment() {
count.incrementAndGet(); // 原子操作
}
public static int getCount() {
return count.get();
}
}解決方案 3:使用 LongAdder(高并發(fā)場(chǎng)景)
import java.util.concurrent.atomic.LongAdder;
public class HighPerformanceCounter {
private static final LongAdder count = new LongAdder();
public static void increment() {
count.increment();
}
public static long getCount() {
return count.sum();
}
}static 代碼塊的線(xiàn)程安全
問(wèn)題:靜態(tài)代碼塊只執(zhí)行一次,但初始化可能涉及復(fù)雜操作
public class UnsafeSingleton {
private static UnsafeSingleton instance;
static {
// 復(fù)雜的初始化邏輯
try {
Thread.sleep(100); // 模擬耗時(shí)操作
instance = new UnsafeSingleton();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private UnsafeSingleton() {}
public static UnsafeSingleton getInstance() {
return instance;
}
}解決方案:使用靜態(tài)內(nèi)部類(lèi)(延遲加載 + 線(xiàn)程安全)
public class ThreadSafeSingleton {
private ThreadSafeSingleton() {}
// 靜態(tài)內(nèi)部類(lèi) - 延遲加載
private static class Holder {
private static final ThreadSafeSingleton INSTANCE = new ThreadSafeSingleton();
}
public static ThreadSafeSingleton getInstance() {
return Holder.INSTANCE;
}
}final 的線(xiàn)程安全特性
final 字段的初始化安全性
public class FinalExample {
private final int x;
private int y;
public FinalExample() {
x = 1; // final 字段在構(gòu)造器完成前對(duì)其他線(xiàn)程可見(jiàn)
y = 2; // 普通字段可能在對(duì)象引用逸出后仍未完成初始化
}
public static void main(String[] args) {
// 線(xiàn)程 A 創(chuàng)建對(duì)象
FinalExample obj = new FinalExample();
// 線(xiàn)程 B 直接讀取對(duì)象
// obj.x 保證可見(jiàn)(1),obj.y 可能是 0
}
}JMM 保證:只要對(duì)象引用對(duì)其他線(xiàn)程可見(jiàn),那么 final 字段也一定初始化完成。
final 不可變對(duì)象的線(xiàn)程安全
public final class ImmutablePerson {
private final String name;
private final int age;
private final List<String> hobbies;
public ImmutablePerson(String name, int age, List<String> hobbies) {
this.name = name;
this.age = age;
// 防御性拷貝
this.hobbies = Collections.unmodifiableList(new ArrayList<>(hobbies));
}
// 只提供 getter,不提供 setter
public String getName() {
return name;
}
public int getAge() {
return age;
}
public List<String> getHobbies() {
return hobbies;
}
}static final 常量的線(xiàn)程安全
public class ThreadSafeConstants {
// 編譯期常量 - 線(xiàn)程安全(只讀)
public static final int MAX_SIZE = 100;
// 運(yùn)行時(shí)常量 - 線(xiàn)程安全(final 保證不可變)
public static final String APP_NAME = "MyApp";
// 不可變集合 - 線(xiàn)程安全
public static final List<String> SAFE_LIST =
Collections.unmodifiableList(Arrays.asList("A", "B", "C"));
// 即使在多線(xiàn)程環(huán)境下訪(fǎng)問(wèn),也不需要同步
public static void main(String[] args) {
new Thread(() -> {
System.out.println(MAX_SIZE); // 安全
}).start();
new Thread(() -> {
System.out.println(APP_NAME); // 安全
}).start();
}
}常見(jiàn)并發(fā)陷阱
陷阱 1:誤以為 final 保證對(duì)象內(nèi)容不可變
public class FinalNotImmutable {
public static void main(String[] args) {
final Map<String, Integer> map = new HashMap<>();
map.put("A", 1); // ? 合法
// 多線(xiàn)程環(huán)境下修改 final 對(duì)象的內(nèi)容 - 不安全!
new Thread(() -> map.put("B", 2)).start();
new Thread(() -> map.put("C", 3)).start();
}
}解決方案:使用不可變集合
public class ImmutableMapExample {
private static final Map<String, Integer> SAFE_MAP;
static {
Map<String, Integer> temp = new HashMap<>();
temp.put("A", 1);
temp.put("B", 2);
SAFE_MAP = Collections.unmodifiableMap(temp); // 不可變
}
public static int getValue(String key) {
return SAFE_MAP.get(key); // 線(xiàn)程安全
}
}陷阱 2:static 初始化的可見(jiàn)性問(wèn)題
public class StaticVisibility {
private static boolean initialized = false;
private static Data data;
static {
data = new Data(); // 初始化數(shù)據(jù)
initialized = true; // 標(biāo)記已初始化
}
public static Data getData() {
if (!initialized) { // 可能讀到舊的 initialized 值
throw new IllegalStateException("Not initialized");
}
return data; // 可能 data 未完成初始化
}
}解決方案:使用 volatile 或確保類(lèi)初始化的原子性
public class StaticVisibilityFixed {
// volatile 保證 happens-before 關(guān)系
private static volatile boolean initialized = false;
private static Data data;
static {
data = new Data();
initialized = true; // volatile 寫(xiě)保證之前的操作對(duì)其他線(xiàn)程可見(jiàn)
}
public static Data getData() {
if (!initialized) {
throw new IllegalStateException("Not initialized");
}
return data;
}
}最佳實(shí)踐
static 使用最佳實(shí)踐
/**
* 工具類(lèi)最佳實(shí)踐
*/
public final class CollectionUtils {
// 私有構(gòu)造器,防止實(shí)例化
private CollectionUtils() {
throw new AssertionError("工具類(lèi)不允許實(shí)例化");
}
// 靜態(tài)工具方法
public static <T> boolean isEmpty(Collection<T> collection) {
return collection == null || collection.isEmpty();
}
public static <T> boolean isNotEmpty(Collection<T> collection) {
return !isEmpty(collection);
}
public static <T> List<T> emptyIfNull(List<T> list) {
return list == null ? Collections.emptyList() : list;
}
}final 使用最佳實(shí)踐
/**
* 不可變對(duì)象最佳實(shí)踐
*/
public final class Money {
private final long amount;
private final String currency;
public Money(long amount, String currency) {
this.amount = amount;
this.currency = Objects.requireNonNull(currency, "currency cannot be null");
}
// 返回新對(duì)象,而不是修改原對(duì)象
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new IllegalArgumentException("Currency mismatch");
}
return new Money(this.amount + other.amount, this.currency);
}
public long getAmount() {
return amount;
}
public String getCurrency() {
return currency;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Money)) return false;
Money money = (Money) o;
return amount == money.amount && currency.equals(money.currency);
}
@Override
public int hashCode() {
return Objects.hash(amount, currency);
}
}單例模式最佳實(shí)踐
/**
* 枚舉單例 - 最佳實(shí)踐
*/
public enum EnumSingleton {
INSTANCE;
private final DataService dataService;
EnumSingleton() {
this.dataService = new DataService();
}
public DataService getDataService() {
return dataService;
}
}性能對(duì)比
關(guān)鍵字組合對(duì)比
| 關(guān)鍵字組合 | 內(nèi)存分配 | 訪(fǎng)問(wèn)速度 | 線(xiàn)程安全 | 適用場(chǎng)景 |
|---|---|---|---|---|
static | 方法區(qū),全局唯一 | 快(直接訪(fǎng)問(wèn)) | 需額外同步 | 工具方法、共享數(shù)據(jù) |
final | 視上下文而定 | 快(編譯器優(yōu)化) | 不可變保證安全 | 不可變數(shù)據(jù)、方法鎖定 |
static final | 方法區(qū),編譯期常量可能內(nèi)聯(lián) | 最快 | 天然安全(只讀) | 全局常量、配置項(xiàng) |
性能測(cè)試示例
public class PerformanceTest {
private static int staticCount = 0;
private int instanceCount = 0;
private static final int STATIC_FINAL_CONSTANT = 100;
public static void testStatic() {
staticCount++; // 直接訪(fǎng)問(wèn)方法區(qū)
}
public void testInstance() {
instanceCount++; // 需要通過(guò)對(duì)象引用
}
public void testConstant() {
int value = STATIC_FINAL_CONSTANT; // 可能被內(nèi)聯(lián)為 int value = 100
}
}總結(jié)
核心要點(diǎn)
static決定歸屬:- 屬于類(lèi),所有實(shí)例共享
- 在類(lèi)加載時(shí)初始化,存儲(chǔ)在方法區(qū)
- 訪(fǎng)問(wèn)速度快,但多線(xiàn)程下需注意線(xiàn)程安全
final決定可變性:- 一旦初始化不可改變
- 提供初始化安全保證
- 適用于不可變對(duì)象設(shè)計(jì)
static final決定本質(zhì):- 全局常量,編譯期可能內(nèi)聯(lián)
- 天然線(xiàn)程安全(只讀)
- 性能最優(yōu)
- 類(lèi)加載時(shí)序:
- 準(zhǔn)備階段:靜態(tài)變量設(shè)為零值,常量設(shè)為最終值
- 初始化階段:按代碼順序執(zhí)行靜態(tài)變量賦值和靜態(tài)代碼塊
- 線(xiàn)程安全:
static變量:需使用synchronized、Atomic或LongAdderstatic final常量:天然線(xiàn)程安全final對(duì)象:引用不可變,但內(nèi)容可能需額外保護(hù)- 靜態(tài)內(nèi)部類(lèi)單例:延遲加載 + 線(xiàn)程安全
記憶口訣
static 歸類(lèi)不歸你,final 不變永相依 static final 全局常,編譯內(nèi)聯(lián)性能強(qiáng) 類(lèi)加載時(shí)分兩步,準(zhǔn)備初始化要清楚 線(xiàn)程安全需注意,原子操作不可欺
參考資料
- Java Language Specification - Classes
- Java Virtual Machine Specification - Class Loading
- Java Concurrency in Practice
- Effective Java (3rd Edition) - Chapter 4
到此這篇關(guān)于Java static 與 final關(guān)鍵字詳解的文章就介紹到這了,更多相關(guān)Java static 與 final內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解Java編程中static關(guān)鍵字和final關(guān)鍵字的使用
- java 中的static關(guān)鍵字和final關(guān)鍵字的不同之處
- java多線(xiàn)程關(guān)鍵字final和static詳解
- Java中final、static關(guān)鍵字與方法的重寫(xiě)和繼承易錯(cuò)點(diǎn)整理
- Java中的static和final關(guān)鍵字的使用詳解
- Java關(guān)鍵字final、static使用總結(jié)
- 學(xué)習(xí)Java的static與final關(guān)鍵字
- Java關(guān)鍵字詳解之final static this super的用法
- Java中static與final關(guān)鍵字詳解(簡(jiǎn)單易懂)
相關(guān)文章
Java采用循環(huán)鏈表結(jié)構(gòu)求解約瑟夫問(wèn)題
這篇文章主要介紹了Java采用循環(huán)鏈表結(jié)構(gòu)求解約瑟夫問(wèn)題的解決方法,是很多Java面試環(huán)節(jié)都會(huì)遇到的經(jīng)典考題,這里詳細(xì)給出了約瑟夫問(wèn)題的原理及Java解決方法,是非常經(jīng)典的應(yīng)用實(shí)例,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12
SpringBoot中REST API 接口傳參的實(shí)現(xiàn)
我們?cè)陂_(kāi)發(fā)?REST API?的過(guò)程中,經(jīng)常需要傳遞參數(shù),本文主要介紹了SpringBoot中REST API 接口傳參的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
Mybatis利用OGNL表達(dá)式處理動(dòng)態(tài)sql的方法教程
這篇文章主要給大家介紹了關(guān)于Mybatis利用OGNL表達(dá)式處理動(dòng)態(tài)sql的方法教程的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。2017-06-06
Spring框架應(yīng)用的權(quán)限控制系統(tǒng)詳解
在本篇文章里小編給大家整理的是關(guān)于基于Spring框架應(yīng)用的權(quán)限控制系統(tǒng)的研究和實(shí)現(xiàn),需要的朋友們可以學(xué)習(xí)下。2019-08-08
SpringBoot Service和Dao的編寫(xiě)詳解
這篇文章主要介紹了SpringBoot Service和Dao的編寫(xiě)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Java生成Echarts表圖的2種實(shí)現(xiàn)方案
這篇文章主要給大家介紹了關(guān)于Java生成Echarts表圖的2種實(shí)現(xiàn)方案,ECharts是一款功能非常強(qiáng)大的JavaScript圖表庫(kù),文中通過(guò)代碼實(shí)例介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09
Java?Unsafe創(chuàng)建對(duì)象的方法實(shí)現(xiàn)
Java中使用Unsafe實(shí)例化對(duì)象是一項(xiàng)十分有趣而且強(qiáng)大的功能,本文主要介紹了Java?Unsafe創(chuàng)建對(duì)象的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
Java數(shù)組隊(duì)列及環(huán)形數(shù)組隊(duì)列超詳細(xì)講解
隊(duì)列是一個(gè)有序列表,可以用數(shù)組和鏈表來(lái)實(shí)現(xiàn),隊(duì)列有一個(gè)原則。即:先存入隊(duì)列的數(shù)據(jù)要先取出,后存入的要后取出,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-09-09

