首页
苏兮影视
随笔记
壁纸
更多
直播
时光轴
友联
关于
统计
Search
1
软件添加id功能按钮
760 阅读
2
v2ray节点搭建
731 阅读
3
typecho非常有特色的模块
549 阅读
4
QQxml消息卡片生成源码
503 阅读
5
网易云音乐歌单ID获取教程
476 阅读
谈天说地
建站源码
经验教程
资源分享
动漫美图
登录
Search
标签搜索
java
flutter
springboot
安卓
rust
linux
vue
docker
joe
快捷键
git
fish shell
maven
redis
netty
dart
groovy
js
设计模式
rpc
尽意
累计撰写
102
篇文章
累计收到
39
条评论
首页
栏目
谈天说地
建站源码
经验教程
资源分享
动漫美图
页面
苏兮影视
随笔记
壁纸
直播
时光轴
友联
关于
统计
搜索到
102
篇与
的结果
2024-09-17
java函数式接口
Java 函数式编程的核心是通过函数式接口实现函数作为参数传递或作为结果返回。函数式接口只有一个抽象方法,Java 8 通过 @FunctionalInterface 注解来确保接口符合该条件。1. Function<T, R>功能:接收一个参数,返回一个结果,通常用于数据转换。方法:R apply(T t):将 T 类型的参数转换为 R 类型的结果。andThen(Function<? super R, ? extends V> after):组合另一个 Function,先执行当前函数,再执行 after。compose(Function<? super V, ? extends T> before):组合另一个 Function,先执行 before,再执行当前函数。适用场景:对象映射、数据转换。示例:Function<String, Integer> stringToLength = String::length; Function<Integer, Integer> square = x -> x * x; Function<String, Integer> combinedFunction = stringToLength.andThen(square); System.out.println(combinedFunction.apply("hello")); // 输出: 252. BiFunction<T, U, R>功能:接收两个参数,返回一个结果。适合处理两个输入的转换或操作。方法:R apply(T t, U u):接收 T 和 U 类型的两个参数,返回 R 类型的结果。andThen(Function<? super R, ? extends V> after):组合另一个 Function,先执行当前函数,再执行 after。适用场景:例如计算两个数的和、将两个对象合并为一个结果。示例:BiFunction<Integer, Integer, Integer> sum = Integer::sum; BiFunction<Integer, Integer, String> sumToString = sum.andThen(String::valueOf); System.out.println(sumToString.apply(3, 5)); // 输出: 83. Consumer<T>功能:接收一个参数,不返回结果,常用于执行一些副作用操作。方法:void accept(T t):对参数执行操作,不返回结果。andThen(Consumer<? super T> after):组合另一个 Consumer,先执行当前操作,再执行 after。适用场景:打印输出、修改对象属性、记录日志。示例:Consumer<String> printConsumer = System.out::println; Consumer<String> upperCaseConsumer = s -> System.out.println(s.toUpperCase()); printConsumer.andThen(upperCaseConsumer).accept("hello"); // 输出: // hello // HELLO4. BiConsumer<T, U>功能:接收两个参数,不返回结果,适合处理两个输入的副作用操作。方法:void accept(T t, U u):对两个参数执行操作,不返回结果。andThen(BiConsumer<? super T, ? super U> after):组合另一个 BiConsumer,先执行当前操作,再执行 after。适用场景:处理两个输入的场景,如将两个参数打印或组合处理。示例:BiConsumer<String, Integer> printBoth = (s, i) -> System.out.println(s + ": " + i); printBoth.accept("Age", 25); // 输出: Age: 255. Supplier<T>功能:不接收参数,返回一个结果。适合用于延迟生成数据或提供默认值。方法:T get():获取结果。适用场景:延迟加载、默认值生成。示例:Supplier<Double> randomSupplier = Math::random; System.out.println(randomSupplier.get()); // 输出: 随机数6. Predicate<T>功能:接收一个参数,返回布尔值,用于条件判断。方法:boolean test(T t):对参数进行条件判断,返回 true 或 false。and(Predicate<? super T> other):组合另一个 Predicate,两个条件都为 true 时返回 true。or(Predicate<? super T> other):组合另一个 Predicate,任意一个条件为 true 时返回 true。negate():返回当前 Predicate 的反结果。适用场景:过滤、条件判断。示例:Predicate<Integer> isEven = n -> n % 2 == 0; Predicate<Integer> isPositive = n -> n > 0; System.out.println(isEven.and(isPositive).test(4)); // 输出: true System.out.println(isEven.or(isPositive).test(-3)); // 输出: true7. BiPredicate<T, U>功能:接收两个参数,返回布尔值,用于条件判断。方法:boolean test(T t, U u):对两个参数进行条件判断,返回 true 或 false。适用场景:比较两个值,或者根据两个值执行条件判断。示例:BiPredicate<String, Integer> lengthGreaterThan = (s, i) -> s.length() > i; System.out.println(lengthGreaterThan.test("hello", 3)); // 输出: true8. UnaryOperator<T>功能:是 Function 的特例,输入输出类型相同,适合用于对输入进行修改和返回同类型的值。方法:T apply(T t):接收 T 类型的参数,返回修改后的 T 类型值。andThen(UnaryOperator<T> after):组合另一个 UnaryOperator,先执行当前操作,再执行 after。compose(UnaryOperator<T> before):组合另一个 UnaryOperator,先执行 before 操作,再执行当前操作。适用场景:对输入进行简单的转换和操作,例如数学运算。示例:UnaryOperator<Integer> square = x -> x * x; System.out.println(square.apply(4)); // 输出: 169. BinaryOperator<T>功能:是 BiFunction 的特例,接收两个相同类型的参数,返回一个相同类型的结果。方法:T apply(T t1, T t2):接收两个相同类型的参数,返回一个相同类型的结果。minBy(Comparator<? super T> comparator):返回一个 BinaryOperator,使用给定的比较器比较两个参数并返回较小的一个。maxBy(Comparator<? super T> comparator):返回一个 BinaryOperator,使用给定的比较器比较两个参数并返回较大的一个。适用场景:用于两个相同类型的数据比较、合并、运算等。示例:BinaryOperator<Integer> sum = Integer::sum; System.out.println(sum.apply(3, 7)); // 输出: 10 BinaryOperator<Integer> max = BinaryOperator.maxBy(Integer::compare); System.out.println(max.apply(3, 7)); // 输出: 710. ToIntFunction<T>功能:接收一个 T 类型参数,返回一个 int 值。常用于从对象中提取整数值。方法:int applyAsInt(T t):接收 T 类型的参数,返回 int 值。适用场景:将对象映射为整数值,如从字符串中提取长度。示例:ToIntFunction<String> stringLengthFunction = String::length; System.out.println(stringLengthFunction.applyAsInt("Hello")); // 输出: 511. ToDoubleFunction<T>功能:接收一个 T 类型参数,返回一个 double 值。常用于从对象中提取浮点数值。方法:double applyAsDouble(T t):接收 T 类型的参数,返回 double 值。适用场景:将对象映射为浮点数值,如从对象中提取某个浮点属性。示例:ToDoubleFunction<Integer> half = n -> n / 2.0; System.out.println(half.applyAsDouble(10)); // 输出: 5.012. IntPredicate功能:接收一个 int 参数,返回布尔值。用于对整数进行条件判断。方法:boolean test(int value):对 int 参数进行条件判断。适用场景:判断整数是否满足某个条件。示例:IntPredicate isEven = n -> n % 2 == 0; System.out.println(isEven.test(4)); // 输出: true13. IntConsumer功能:接收一个 int 参数,不返回结果。适合对整数进行某些操作。方法:void accept(int value):对 int 参数执行操作。andThen(IntConsumer after):组合另一个 IntConsumer,先执行当前操作,再执行 after。适用场景:处理或记录 int 类型的数据,比如打印、操作日志等。示例:IntConsumer printInt = System.out::println; IntConsumer doubleAndPrint = i -> System.out.println(i * 2); printInt.andThen(doubleAndPrint).accept(5); // 输出: // 5 // 1014. IntFunction<R>功能:接收一个 int 参数,返回一个结果。用于将整数转换为其他类型的对象。方法:R apply(int value):接收 int 参数,返回 R 类型结果。适用场景:将整数映射为其他对象类型,比如将索引映射到某个值。示例:IntFunction<String> intToString = Integer::toString; System.out.println(intToString.apply(10)); // 输出: "10"15. IntSupplier功能:不接收参数,返回一个 int 值。常用于生成整数值。方法:int getAsInt():获取结果。适用场景:生成随机数、序列号或延迟生成某个整数值。示例:IntSupplier randomInt = () -> (int) (Math.random() * 100); System.out.println(randomInt.getAsInt()); // 输出: 随机整数16. LongFunction<R>功能:接收一个 long 参数,返回一个结果。适合将 long 类型的数据转换为其他类型的对象。方法:R apply(long value):接收 long 参数,返回 R 类型结果。适用场景:类似于 IntFunction,但处理的是 long 数据。示例:LongFunction<String> longToString = Long::toString; System.out.println(longToString.apply(100000L)); // 输出: "100000"17. DoublePredicate功能:接收一个 double 参数,返回布尔值。用于对浮点数进行条件判断。方法:boolean test(double value):对 double 参数进行条件判断。适用场景:判断浮点数是否满足某个条件。示例:DoublePredicate isPositive = d -> d > 0.0; System.out.println(isPositive.test(3.14)); // 输出: true18. DoubleConsumer功能:接收一个 double 参数,不返回结果。适合对浮点数进行某些操作。方法:void accept(double value):对 double 参数执行操作。andThen(DoubleConsumer after):组合另一个 DoubleConsumer,先执行当前操作,再执行 after。适用场景:类似于 IntConsumer,但处理的是 double 数据。示例:DoubleConsumer printDouble = System.out::println; DoubleConsumer halfAndPrint = d -> System.out.println(d / 2); printDouble.andThen(halfAndPrint).accept(8.0); // 输出: // 8.0 // 4.019. DoubleFunction<R>功能:接收一个 double 参数,返回一个结果。用于将 double 数据转换为其他类型的对象。方法:R apply(double value):接收 double 参数,返回 R 类型结果。适用场景:将浮点数映射为其他对象类型。示例:DoubleFunction<String> doubleToString = Double::toString; System.out.println(doubleToString.apply(3.1415)); // 输出: "3.1415"20. BinaryOperator<T>功能:接收两个相同类型的参数,返回一个相同类型的结果。是 BiFunction<T, T, T> 的特例,常用于合并或比较两个值。方法:T apply(T t1, T t2):接收两个相同类型的参数,返回一个相同类型的结果。minBy(Comparator<? super T> comparator):返回使用给定比较器的较小值。maxBy(Comparator<? super T> comparator):返回使用给定比较器的较大值。适用场景:合并或比较两个相同类型的对象,例如求最大值或最小值。示例:BinaryOperator<Integer> min = BinaryOperator.minBy(Integer::compare); System.out.println(min.apply(3, 5)); // 输出: 3总结Java 函数式编程提供了一系列的函数式接口,覆盖了各种常见的操作场景,如数据转换、条件判断、副作用操作等。通过这些接口,开发者可以更方便地使用 Lambda 表达式进行简洁、高效的编程。这些接口大部分都支持组合操作,如 andThen 和 compose,使得操作链条更加灵活。在实际应用中,选择合适的函数式接口可以极大简化代码逻辑,提高可读性与维护性。
2024年09月17日
18 阅读
0 评论
1 点赞
java原子性变量
AtomicIntegerget(): 获取当前值。int value = atomicInteger.get();set(int newValue): 设置新值。atomicInteger.set(42);incrementAndGet(): 将当前值加一,并返回新值。int newValue = atomicInteger.incrementAndGet();decrementAndGet(): 将当前值减一,并返回新值。int newValue = atomicInteger.decrementAndGet();addAndGet(int delta): 将当前值加上指定增量,并返回新值。int newValue = atomicInteger.addAndGet(10);compareAndSet(int expect, int update): 如果当前值等于 expect,则将其更新为 update,并返回是否成功。boolean success = atomicInteger.compareAndSet(10, 20);AtomicLongget(): 获取当前值。long value = atomicLong.get();set(long newValue): 设置新值。atomicLong.set(100L);incrementAndGet(): 将当前值加一,并返回新值。long newValue = atomicLong.incrementAndGet();decrementAndGet(): 将当前值减一,并返回新值。long newValue = atomicLong.decrementAndGet();addAndGet(long delta): 将当前值加上指定增量,并返回新值。long newValue = atomicLong.addAndGet(50L);compareAndSet(long expect, long update): 如果当前值等于 expect,则将其更新为 update,并返回是否成功。boolean success = atomicLong.compareAndSet(100L, 200L);AtomicBooleanget(): 获取当前布尔值。boolean value = atomicBoolean.get();set(boolean newValue): 设置新布尔值。atomicBoolean.set(true);compareAndSet(boolean expect, boolean update): 如果当前值等于 expect,则将其更新为 update,并返回是否成功。boolean success = atomicBoolean.compareAndSet(false, true);AtomicReference<V>get(): 获取当前引用。V value = atomicReference.get();set(V newValue): 设置新引用。atomicReference.set(newValue);compareAndSet(V expect, V update): 如果当前引用等于 expect,则将其更新为 update,并返回是否成功。boolean success = atomicReference.compareAndSet(expectedValue, newValue);getAndSet(V newValue): 设置新引用并返回旧引用。V oldValue = atomicReference.getAndSet(newValue);AtomicMarkableReference<V>getReference(): 获取当前引用。V reference = markableReference.getReference();isMarked(): 获取标记。boolean marked = markableReference.isMarked();set(V newReference, boolean newMark): 设置新引用和标记。markableReference.set(newReference, true);compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark): 如果当前引用和标记匹配 expectedReference 和 expectedMark,则更新为 newReference 和 newMark,并返回是否成功。boolean success = markableReference.compareAndSet(expectedReference, newReference, false, true);AtomicStampedReference<V>getReference(): 获取当前引用。V reference = stampedReference.getReference();getStamp(): 获取当前戳。int stamp = stampedReference.getStamp();set(V newReference, int newStamp): 设置新引用和戳。stampedReference.set(newReference, 1);compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp): 如果当前引用和戳匹配 expectedReference 和 expectedStamp,则更新为 newReference 和 newStamp,并返回是否成功。boolean success = stampedReference.compareAndSet(expectedReference, newReference, 0, 1);
2024年09月16日
62 阅读
0 评论
2 点赞
java线程池
Java线程池是在处理并发编程时非常关键的一部分,主要用于管理被多个任务共享的一组固定数量的线程资源。线程池的关键参数详解核心线程数(corePoolSize)这是线程池初始化时的线程数量,即使这些线程处于空闲状态,也不会被回收。这个参数对系统性能有重要影响,设置过小可能导致处理任务时延迟增加,设置过大可能会浪费系统资源。最大线程数(maximumPoolSize)线程池允许同时运行的最大线程数量。当工作队列满了且当前运行的线程数小于最大线程数时,线程池会创建新的线程来处理任务。存活时间(keepAliveTime)和时间单位(unit)当线程数量超过核心线程数时,这是超出数量的空闲线程在被终止前可以保持空闲的最长时间。单位是一个枚举,表示时间单位,如TimeUnit.SECONDS。工作队列(workQueue)用于存放等待执行的任务的阻塞队列。常见的选项包括直接交付队列SynchronousQueue、无界队列LinkedBlockingQueue和有界队列ArrayBlockingQueue。线程工厂(ThreadFactory)创建新线程的工厂。可以自定义线程工厂来设置线程的名字、优先级等属性。拒绝策略(RejectedExecutionHandler)当队列满了并且线程数达到最大值时,新提交的任务如何处理。常见的策略有ThreadPoolExecutor.AbortPolicy(抛出异常)、ThreadPoolExecutor.DiscardPolicy(丢弃任务,不抛出异常)、ThreadPoolExecutor.DiscardOldestPolicy(丢弃队列最前面的任务),和ThreadPoolExecutor.CallerRunsPolicy(调用者所在的线程来执行任务)。示例:使用ThreadPoolExecutor自定义线程池以下是如何直接使用ThreadPoolExecutor来创建一个自定义的线程池的示例:import java.util.concurrent.*; public class ThreadPoolDemo { public static void main(String[] args) { int corePoolSize = 2; int maximumPoolSize = 4; long keepAliveTime = 10; TimeUnit unit = TimeUnit.SECONDS; BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2); ThreadFactory threadFactory = Executors.defaultThreadFactory(); RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); ExecutorService threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); for (int i = 0; i < 10; i++) { int taskNo = i; threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + " is executing task " + taskNo); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } }); } threadPool.shutdown(); } }Executors类常用工厂方法newFixedThreadPool(int nThreads)创建一个固定线程数的线程池。所有提交的任务都使用这些线程处理。适用于任务量预知,需要控制并发数量的场景。示例代码:ExecutorService executor = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { int taskNo = i; executor.execute(() -> { System.out.println(Thread.currentThread().getName() + " is executing task " + taskNo); try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } executor.shutdown();newSingleThreadExecutor()创建一个单线程的执行器,这可以保证所有任务按照提交顺序串行执行。适用于需要单线程执行任务,保证顺序性和一致性的场景。示例代码:ExecutorService executor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 5; i++) { int taskNo = i; executor.execute(() -> { System.out.println("Executing task " + taskNo); try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } executor.shutdown();newCachedThreadPool()创建一个可缓存线程的线程池,如果线程池的大小超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。适用于任务数动态变化的场景。示例代码:ExecutorService executor = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { int taskNo = i; executor.execute(() -> { System.out.println(Thread.currentThread().getName() + " is executing task " + taskNo); try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } executor.shutdown();newScheduledThreadPool(int corePoolSize)创建一个固定数量的线程来执行定时或周期性任务。适用于需要多个后台线程执行周期任务,同时为了资源的有效利用需要限制线程数量的场景。示例代码:ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); Runnable beeper = () -> System.out.println("beep"); // 安排任务每10秒钟执行一次 ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(beeper, 0, 10, TimeUnit.SECONDS); // 安排在60秒后取消任务 scheduler.schedule(() -> beeperHandle.cancel(true), 60, TimeUnit.SECONDS);关键方法execute(Runnable command): 提交一个Runnable任务用于执行。submit(Callable task): 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的Future。shutdown(): 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。shutdownNow(): 尝试停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。状态管理线程池的状态变化是通过内部的runState和workerCount进行管理的,不同状态下线程池的行为也不同,比如在SHUTDOWN状态下,线程池不接受新任务,但会继续处理队列中的旧任务。使用建议和最佳实践资源管理:对于newCachedThreadPool和newScheduledThreadPool等可能创建大量线程的工厂方法,需要注意系统资源的使用,避免创建过多线程导致资源耗尽。任务类型和线程池策略匹配:选择最适合任务特性的线程池类型,如IO密集型、CPU密集型或混合型任务。优雅关闭:在应用程序结束时,应调用shutdown()或shutdownNow()方法来优雅地关闭线程池,释放资源。通过合理选择和使用Executors类提供的工厂方法,可以有效地管理线程资源,提高程序的并发性能和稳定性。
2024年09月16日
70 阅读
0 评论
1 点赞
Java 设计模式之享元模式详解
1. 定义享元模式(Flyweight Pattern)是一种结构型设计模式,其核心思想是通过共享对象来减少内存的使用和提高系统性能。享元模式通过将内部可共享的状态和外部独立的状态分离,从而减少对象的重复创建。2. 适用场景享元模式适用于以下情况:对象数量庞大,且大多数对象可以共享部分状态。内存消耗高,有必要通过对象复用来优化性能。外部状态可独立存储,共享的对象可以被多个外部对象同时使用。3. 模式结构享元模式主要由以下几个部分构成:Flyweight接口:定义享元对象的接口,包含需要实现的业务方法。ConcreteFlyweight(具体享元类):实现 Flyweight 接口,提供共享的内部状态。UnsharedConcreteFlyweight(非共享享元类):定义那些不被共享的对象。FlyweightFactory(享元工厂类):用于管理享元对象,提供共享对象的缓存机制。Client(客户端):负责存储和操作外部状态,并使用享元对象完成具体任务。4. 享元模式的核心:内部状态与外部状态内部状态:可以共享的部分,不会随外部变化。由享元对象内部维护。外部状态:不可共享的部分,会随环境改变。由客户端维护或传递给享元对象。5. 实例:JDK 中 Integer 的缓存机制在 Java 中,Integer 类为了提高性能和减少内存消耗,对 -128 到 127 之间的整数进行了缓存,这是享元模式的一个典型应用。5.1 Integer 缓存的工作原理当你对一个 Integer 对象进行自动装箱或通过 Integer.valueOf() 创建时,JVM 会检查数字是否在缓存范围(-128 到 127)内:如果在此范围内,返回缓存中的对象。如果超出该范围,创建新的 Integer 对象。Integer i1 = 127; // 自动装箱,相当于 Integer.valueOf(127) Integer i2 = 127; // 直接从缓存中获取 Integer i3 = 128; // 创建新的对象,因为超出缓存范围 Integer i4 = 128; // 再次创建新的对象5.2 Integer 缓存的源码Integer.valueOf() 方法通过内部的 IntegerCache 类来实现缓存机制:public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) { return IntegerCache.cache[i + (-IntegerCache.low)]; } return new Integer(i); } private static class IntegerCache { static final int low = -128; static final int high = 127; static final Integer cache[]; static { cache = new Integer[(high - low) + 1]; for (int i = 0, j = low; j <= high; i++, j++) { cache[i] = new Integer(j); } } }在此代码中,IntegerCache 类缓存了 -128 到 127 之间的 Integer 对象。调用 Integer.valueOf() 时,如果数字在缓存范围内,则返回缓存对象,避免创建新的对象。5.3 实例演示public class FlyweightExample { public static void main(String[] args) { Integer i1 = Integer.valueOf(127); Integer i2 = Integer.valueOf(127); Integer i3 = Integer.valueOf(128); Integer i4 = Integer.valueOf(128); System.out.println(i1 == i2); // true, 127在缓存范围内,共享对象 System.out.println(i3 == i4); // false, 128不在缓存范围内,不同对象 } }输出结果:true false5.4 内部状态与外部状态的应用内部状态:在 -128 到 127 之间的整数是共享的,表示可重复使用的部分(享元模式中的共享对象)。外部状态:超出此范围的整数需要每次新建,是不可共享的部分。6. 享元模式总结优点:减少内存开销:通过共享相同的对象,减少了重复对象的创建,尤其在大量小对象场景中非常有效。提高性能:减少了对象的创建和销毁过程,尤其在大量使用相似对象时性能提升显著。缺点:增加代码复杂度:需要区分内部状态和外部状态,额外的状态管理会增加代码复杂度。适用场景有限:仅在对象可共享且变化不大时效果显著,不适用于复杂对象或场景。7. 适用案例JDK 中 Integer 的缓存机制:Integer 类缓存了 -128 到 127 之间的整数,避免重复创建相同数值的对象,节省内存。文本编辑器中的字符对象:同样字符的对象可以共享字体、大小等属性,只需根据不同位置设定外部状态(坐标)。游戏中的精灵对象:相同的精灵图片资源可以共享,不同精灵的位置和行为通过外部状态来控制。8. 模式应用要点享元模式的核心是通过分离共享的内部状态和不可共享的外部状态来实现对象的重用。在实际开发中,常见的场景如缓存、池化资源管理等,都可以通过享元模式来实现内存优化。
2024年09月16日
23 阅读
0 评论
2 点赞
java浅拷贝深拷贝
在Java中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是两个常见的对象拷贝方式。它们的主要区别在于它们处理对象中的引用类型字段的方式。1. 浅拷贝(Shallow Copy)浅拷贝仅拷贝对象本身以及对象中所有的基本数据类型字段,对于引用类型字段(如对象、数组等),仅复制其引用(即指针),而不复制实际的对象。这意味着原对象和拷贝对象中的引用类型字段指向同一个内存地址,因此改变一个对象中的引用类型字段可能会影响到另一个对象。实现方式:实现Cloneable接口并重写clone()方法。代码示例:class Address { String city; public Address(String city) { this.city = city; } } class Person implements Cloneable { String name; Address address; public Person(String name, Address address) { this.name = name; this.address = address; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); // 浅拷贝 } } public class ShallowCopyExample { public static void main(String[] args) throws CloneNotSupportedException { Address address = new Address("New York"); Person person1 = new Person("John", address); Person person2 (Person) person1.clone(); System.out.println(person1.address.city); // New York System.out.println(person2.address.city); // New York person2.address.city = "San Francisco"; System.out.println(person1.address.city); // San Francisco System.out.println(person2.address.city); // San Francisco } }应用场景:浅拷贝适用于对象结构简单且不包含复杂的嵌套对象的情况。因为浅拷贝的速度较快,适合对性能有较高要求但不涉及深层嵌套结构的场景。2. 深拷贝(Deep Copy)深拷贝不仅拷贝对象本身,还会递归地拷贝对象中所有引用类型字段所指向的对象。这意味着原对象和拷贝对象中的引用类型字段互不影响,修改一个对象中的引用类型字段不会影响到另一个对象。实现方式:重写clone()方法,手动拷贝所有的引用类型字段。使用序列化和反序列化实现深拷贝。代码示例:class Address implements Cloneable { String city; public Address(String city) { this.city = city; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class Person implements Cloneable { String name; Address address; public Person(String name, Address address) { this.name = name; this.address = address; } @Override protected Object clone() throws CloneNotSupportedException { Person clonedPerson = (Person) super.clone(); clonedPerson.address = (Address) address.clone(); // 深拷贝 return clonedPerson; } } public class DeepCopyExample { public static void main(String[] args) throws CloneNotSupportedException { Address address = new Address("New York"); Person person1 = new Person("John", address); Person person2 = (Person) person1.clone(); System.out.println(person1.address.city); // New York System.out.println(person2.address.city); // New York person2.address.city = "San Francisco"; System.out.println(person1.address.city); // New York System.out.println(person2.address.city); // San Francisco } }应用场景:深拷贝适用于对象结构复杂且包含多个嵌套引用类型字段的场景,尤其是在这些引用类型字段是可变对象时。深拷贝确保拷贝后的对象与原对象完全独立,不会互相影响。总结浅拷贝:仅复制对象及其基本数据类型字段,引用类型字段只复制引用。适用于简单的对象结构。深拷贝:复制对象及其所有引用类型字段所指向的对象。适用于复杂对象结构,确保深层次的独立性。根据具体需求选择合适的拷贝方式可以有效地避免不必要的副作用并提高程序的性能。
2024年08月25日
68 阅读
0 评论
1 点赞
1
...
6
7
8
...
21