中山网站建设文化信息公司有必要做官网吗
web/
2025/9/28 21:48:21/
文章来源:
中山网站建设文化信息,公司有必要做官网吗,企业网站管理的含义,wordpress怎么缩进目录 四. 强引用#xff0c;软引用#xff0c;弱引用#xff0c;幻象引用之间的区别#xff1f; 1.前言 2.强引用 2.1 强引用赋值为null 2.2 超过了引用的作用域 2.2.1 描述#xff1a; 2.2.2 作用域内#xff1a; 2.2.3 不在作用域内: 3. 软引用#xff08;SoftRefere… 目录 四. 强引用软引用弱引用幻象引用之间的区别 1.前言 2.强引用 2.1 强引用赋值为null 2.2 超过了引用的作用域 2.2.1 描述 2.2.2 作用域内 2.2.3 不在作用域内: 3. 软引用SoftReference 3.1 描述 4. 弱引用WeakReference 4.1 解析 5. 幻像引用 5.1 描述 5.2 流程图 5.3 解析 5.3.1 引用队列ReferenceQueue的作用主要体现在以下两个方面 5.3 .2 监控对象的销毁并统计对象的创建和销毁次数 5.3.3 对象清理 5.4 流程图解析 5.4.1 强可达Strongly Reachable 5.4.2 软可达Softly Reachable 5.4.3 弱可达Weakly Reachable 5.4.4幻象可达Phantom Reachable 5.4.5 最后的状态 五. String,StringBuffer,StringBuilder之间的区别 1.String 1.1 解析 1.1.1lmmutable类 1.1.2 好处 1.1.3 注意 1.1.4 字符串操作对应用性能有影响的原因主要包括以下几个方面 1.1.4.1 内存开销 1.1.4.2字符串拷贝 1.1.4.3 时间复杂度 2. StringBuffer 3. StringBuilder 4. 考点分析 5. 总结 5.1 String 5.1.1 String的创建机理 5.1.2 其运行机制是 5.1.3 举例 5.1.4 String的特性 5.1.4.1 不可变 5.1.4.2针对常量池的优化 5.2 StringBuffer/StringBuilder 5.3 应用场景 5.3.1 使用StringBuffer例如HTTP参数解析与封装 5.3.1.1 多线程环境下使用同步的 StringBuffer 进行字符串拼接的例子 5.3.2 使用 StringBuilder 进行SQL语句拼装和JSON封装的示例 5.3.2.1 SQL拼接 5.3.2.2 JSON封装 四. 强引用软引用弱引用幻象引用之间的区别 1.前言
Java语言中除了原始数据类型的变量其他都是引用类型指向各种不同的对象理解引用可以帮助掌握Java对象生命周期和JVM内部相关机制
2.强引用
不同引用类型主要体现的是对象不同的可达性reachable状态对垃圾收集的影响
强引用Strong Reference即是我们常见的普通对象引用只要存在强引用指向一个对象就表明对象还活着垃圾收集器不会碰这种对象对于一个普通的对象如果没有其他引用关系只要进行超过了引用的作用域或者将强引用赋值为null就可以被垃圾回收器收集。
解析内容
2.1 强引用赋值为null
假设我们有一个Java类Person
代码如下
public class Person {private String name;public Person(String name) {this.name name;}public String getName() {return name;}
}现在我们创建一个强引用指向该对象
Person person new Person(John);此时对象John拥有一个强引用person因此垃圾收集器不会回收该对象。
如果我们将强引用置为null
person null;此时没有任何强引用指向对象John它成为了垃圾对象可以被垃圾收集器回收。具体的回收时机会根据垃圾收集策略来确定。 2.2 超过了引用的作用域
2.2.1 描述
当引用超出其作用域时意味着该引用无法被访问到。在Java中一个对象的作用域通常是定义它的代码块或方法。
2.2.2 作用域内
public class ScopeExample {public static void main(String[] args) {Person person; // 在作用域外声明引用{person new Person(John);System.out.println(person.getName()); // 输出John}// 在作用域外仍然可以使用person引用System.out.println(person.getName());}
}2.2.3 不在作用域内:
public class ScopeExample {public static void main(String[] args) {{Person person new Person(John);System.out.println(person.getName()); // 输出John}// 此处无法访问person引用超出了其作用域// System.out.println(person.getName()); }
}3. 软引用SoftReference
3.1 描述
相对于强引用引用弱化一些的引用可以让对象进行一些垃圾收集只有当JVM认为内存不足的时候会尝试进行回收软引用指向的对象。
JVM会确保在抛出OutOfMemoryError之前进行清理软引用指向的对象。
软引用通常用来实现内存敏感的缓存如果还有空间内存可以暂时保留缓存当内存不足的时候清理掉保证了使用缓存的同时不会耗尽内存。 4. 弱引用WeakReference
不能使对象豁免垃圾收集提供一种访问在弱引用状态下对象的途径构建一种没有特点约束关系维护一种非维护的映射关系如果试图获取对象还在就使用它否则重现实例化。
4.1 解析
定义了一个ExpensiveObject类表示一个耗费资源的对象。通过定义get_object函数和cache字典作为缓存我们可以使用弱引用来构建非强制性的映射关系。当需要获取对象时首先检查缓存中是否有对应的弱引用对象如果存在且未被垃圾回收则直接返回该对象否则重新实例化对象并将其添加到缓存中。
需要注意的是由于弱引用并不能阻止对象被垃圾回收当对象被垃圾回收后弱引用将返回None。因此在使用弱引用构建缓存时需要在适当的时机进行缓存项的更新和清理以保证缓存数据的正确性和有效性。
import java.lang.ref.WeakReference;class ExpensiveObject {private String name;public ExpensiveObject(String name) {this.name name;}public String getName() {return name;}
}public class WeakReferenceExample {public static void main(String[] args) {// 缓存示例WeakReferenceExpensiveObject cache new WeakReference(null);ExpensiveObject obj1 getOrCreateObject(Example);System.out.println(obj1.getName()); // 输出: ExampleExpensiveObject obj2 getOrCreateObject(Example);System.out.println(obj2.getName()); // 输出: Example// 因为缓存中已经存在名为Example的对象第二次获取时直接从缓存中获取并不会重复创建}public static ExpensiveObject getOrCreateObject(String name) {ExpensiveObject obj null;// 从缓存中获取弱引用对象WeakReferenceExpensiveObject objRef cache.get();if (objRef ! null) {obj objRef.get();}if (obj null) {// 如果缓存中没有对应的对象或对象已经被垃圾回收了重新创建并缓存obj new ExpensiveObject(name);cache new WeakReference(obj);}return obj;}
}5. 幻像引用
5.1 描述
也叫虚引用不能通过他访问对象幻像引用提供了一种确保对象被finalize后做某些事情的机制可以用幻象引用监控对象的创建和销毁
5.2 流程图 强引用 5.3 解析
5.3.1 引用队列ReferenceQueue的作用主要体现在以下两个方面 监控对象的销毁通过创建ReferenceQueueTrackedObject类型的引用队列referenceQueue并将幻象引用对象注册到引用队列中 phantomReference new PhantomReference(this, referenceQueue); 当TrackedObject对象被垃圾回收器标记为可回收时对应的幻象引用就会被放入引用队列中。通过检查引用队列中是否存在幻象引用我们可以得知对象已经被finalize并且即将被回收。 统计对象的创建和销毁次数ObjectTracker类中的trackObjectCreation()和trackObjectDestruction()方法用于跟踪对象的创建和销毁次数。在TrackedObject的构造方法中当对象被创建时会调用ObjectTracker.trackObjectCreation()方法增加创建次数。而在finalize方法中会调用destroy()方法进而调用ObjectTracker.trackObjectDestruction()方法增加销毁次数。
通过结合引用队列、幻象引用和ObjectTracker类我们可以在程序中监控对象的创建和销毁情况并获取相应的统计数据。在示例中通过输出ObjectTracker.getCreatedCount()和ObjectTracker.getDestroyedCount()我们可以分别得到对象的创建次数和销毁次数。
引用队列在该示例中的主要用途是监控对象的销毁并统计对象的创建和销毁次数。
通过引用队列和幻象引用的配合使用我们可以实现更精确的对象跟踪和资源管理。
循环来检查引用队列中是否有幻象引用。通过调用referenceQueue.poll()方法我们可以从引用队列中获取幻象引用。如果获取到了幻象引用则意味着对象已经被finalize并且即将被回收
5.3 .2 监控对象的销毁并统计对象的创建和销毁次数
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;class ObjectTracker {private static int createdCount 0;private static int destroyedCount 0;public static void trackObjectCreation() {createdCount;}public static void trackObjectDestruction() {destroyedCount;}public static int getCreatedCount() {return createdCount;}public static int getDestroyedCount() {return destroyedCount;}
}class TrackedObject {private String name;private PhantomReferenceTrackedObject phantomReference;public TrackedObject(String name, ReferenceQueueTrackedObject referenceQueue) {this.name name;phantomReference new PhantomReference(this, referenceQueue);ObjectTracker.trackObjectCreation();}public void destroy() {ObjectTracker.trackObjectDestruction();}
}public class PhantomReferenceMonitoringExample {public static void main(String[] args) throws InterruptedException {ReferenceQueueTrackedObject referenceQueue new ReferenceQueue();// 创建对象并跟踪创建次数TrackedObject obj1 new TrackedObject(Object 1, referenceQueue);TrackedObject obj2 new TrackedObject(Object 2, referenceQueue);System.out.println(Created count: ObjectTracker.getCreatedCount()); // 输出: Created count: 2// 销毁对象并跟踪销毁次数obj1.destroy();obj2.destroy();// 清理引用队列判断是否存在幻象引用PhantomReference? phantomRef;while ((phantomRef (PhantomReference?) referenceQueue.poll()) ! null) {System.out.println(Object finalized and ready for destruction: phantomRef);}System.out.println(Destroyed count: ObjectTracker.getDestroyedCount()); // 输出: Destroyed count: 2}
}5.3.3 对象清理
定义了一个ExpensiveObject类其中重写了finalize方法在对象被垃圾回收前会执行该方法。通过创建一个幻象引用PhantomReference并指定相应的引用队列ReferenceQueue我们可以在程序中检查对象是否已经被 finalize。
在主函数中模拟进行垃圾回收操作并等待一段时间以确保垃圾回收完成。然后从引用队列中尝试获取幻象引用所引用的对象如果对象已经被 finalize则可以通过引用队列获取到幻象引用否则对象仍然存在。
幻象引用通常与引用队列结合使用以便在对象被 finalize 后执行特定的操作
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;class ExpensiveObject {private String name;public ExpensiveObject(String name) {this.name name;}public String getName() {return name;}Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println(Object name is finalized.);}
}public class PhantomReferenceExample {public static void main(String[] args) throws InterruptedException {ReferenceQueueExpensiveObject referenceQueue new ReferenceQueue();PhantomReferenceExpensiveObject phantomReference new PhantomReference(new ExpensiveObject(Example), referenceQueue);// 模拟垃圾回收动作System.gc();Thread.sleep(1000); // 等待垃圾回收完成// 检查是否已经被finalizeif (referenceQueue.poll() ! null) {System.out.println(Object has been finalized.);} else {System.out.println(Object still exists.);}}
}5.4 流程图解析
5.4.1 强可达Strongly Reachable
就是当一个对象可以有一个或多个线程可以不通过各种 引用访问到的情况。比如我们新创建一个对象那么创建它的线程对它就是强可达。 5.4.2 软可达Softly Reachable
就是当我们只能通过软引用才能访问到对象的状态。 5.4.3 弱可达Weakly Reachable
类似前面提到的就是无法通过强引用或者软引用访问 只能通过弱引用访问时的状态。这是十分临近 finalize 状态的时机当弱引用被清除的时 候就符合 finalize 的条件了。 5.4.4幻象可达Phantom Reachable
上面流程图已经很直观了就是没有强、软、弱引用 关联并且 finalize 过了只有幻象引用指向这个对象的时候。
5.4.5 最后的状态
就是不可达unreachable意味着对象可以被清除了。
除了幻象引用因为 get 永远返回 null如果对象还没有被销毁都可以通过 get 方法获 取原有对象。这意味着利用软引用和弱引用我们可以将访问到的对象重新指向强引用 也就是人为的改变了对象的可达性状态 五. String,StringBuffer,StringBuilder之间的区别 1.String
String是Java语言非常重要的类提供了构造和管理字符串的各种逻辑。
典型的lmmutable类被声明成final class所有属性也都是final由于不可变性类似拼接剪裁字符串等动作都会产生新的String对象。
由于字符串操作的普遍性所以相关操作的效率往往对应用性能有明细的影响。 1.1 解析
1.1.1lmmutable类
Immutable 类是指在对象创建后其状态无法被修改的类。换句话说Immutable 类的实例一旦创建就不能被改变。对于 String 类来说它被设计为 Immutable 类意味着一旦创建了一个 String 对象其中包含的字符序列就不能被改变。
1.1.2 好处
线程安全由于 String 类是不可变的多线程环境下可以保证对象内容的不变性从而避免了同步操作。缓存利用由于字符串常量是不可变的因此可以被缓存起来提高程序的性能和效率。安全性String 对象作为方法参数时不会被修改保证了数据的安全性。
1.1.3 注意
虽然 String 对象本身是不可变的但是通过反射等手段可以绕过限制直接修改对象的内部状态。所以在涉及到安全性要求较高的场景中还是需要额外的措施来保证对象的不可变性。
1.1.4 字符串操作对应用性能有影响的原因主要包括以下几个方面
1.1.4.1 内存开销
由于 String 类是不可变的每次对字符串进行拼接、剪裁等操作都会创建一个新的字符串对象。这就导致了频繁的内存分配和释放增加了垃圾回收的压力消耗了额外的内存空间。
1.1.4.2字符串拷贝
在字符串拼接或者剪裁时通常涉及到将多个字符串合并为一个新的字符串或者从原字符串中截取一部分形成新的字符串。这些操作都需要将原始字符串的内容复制到新的字符串对象中如果字符串长度较大或者操作频繁将会产生大量的内存拷贝操作降低了性能。
1.1.4.3 时间复杂度
某些字符串操作的时间复杂度较高例如字符串拼接操作的时间复杂度为 O(n^2)其中 n 为字符串的长度。当需要拼接大量的字符串时操作的时间复杂度会呈现出指数级增长导致性能下降
2. StringBuffer
解决拼接产生太多中间对象的问题而提供的一个类可以使用append或者add方法将字符串添加到已有序列的末尾或者指定位置。
Stringbuffer本质是一个线程安全的可修改字符序列保证了线程安全但是也带来了额外的性能开销所以除非有线程安全的需要不然还是采用StringBuilder
3. StringBuilder
Java1.5新增加的在能力上面和StringBuffer没有本质区别去掉了线程安全的部分有效的减小了开销绝大部分情况下字符串拼接的首选。
4. 考点分析
应用开发离不开操作字符串理解字符串的设计和实现以及相关工具拼接类的使用对于写出高质量代码很有帮助。
5. 总结
5.1 String
5.1.1 String的创建机理 由于String在Java世界中使用过于频繁Java为了避免在一个系统中产生大量的String对象 引入了字符串常量池。
5.1.2 其运行机制是
创建一个字符串时首先检查池中是否有值相同的字符串对象如果有则不需要创建直接从池中刚查找到的对象引用如果没有则新建字符串对象返回对象引用并且将新创建的对象放入池中。但是通过new方法创建的String对象是不检查字符串池的而是直接在堆区或栈区创建一个新的对象也不会把对象放入池中。
上述原则只适用于通过直接量给String对象引用赋值的情况。 5.1.3 举例
String str1 123; //通过直接量赋值方式放入字符串常量池 String str2 new String(“123”);//通过new方式赋值方式不放入字符串常量池 注意String提供了inter()方法。调用该方法时如果常量池中包括了一个等于此String对象 的字符串由equals方法确定则返回池中的字符串。否则将此String对象添加到池中 并且返回此池中对象的引用
5.1.4 String的特性 5.1.4.1 不可变
指String对象一旦生成则不能再对它进行改变。不可变的主要作用在于当一 个对象需要被多线程共享并且访问频繁时可以省略同步和锁等待的时间从而大幅度提 高系统性能。不可变模式是一个可以提高多线程程序的性能降低多线程程序复杂度的设计 模式。 5.1.4.2针对常量池的优化
当2个String对象拥有相同的值时他们只引用常量池中的同一个拷 贝。当同一个字符串反复出现时这个技术可以大幅度节省内存空间
5.2 StringBuffer/StringBuilder StringBuffer和StringBuilder都实现了AbstractStringBuilder抽象类拥有几乎一致对外提供的 调用接口。
其底层在内存中的存储方式与String相同都是以一个有序的字符序列char类型 的数组进行存储不同点是StringBuffer/StringBuilder对象的值是可以改变的并且值改变 以后对象引用不会发生改变;两者对象在构造过程中首先按照默认大小申请一个字符数 组由于会不断加入新数据当超过默认大小后会创建一个更大的数组并将原先的数组 内容复制过来再丢弃旧的数组。因此对于较大对象的扩容会涉及大量的内存复制操作 如果能够预先评估大小可提升性能。 唯一需要注意的是StringBuffer是线程安全的但是StringBuilder是线程不安全的。
StringBuffer类中方法定义前面都会有synchronize关键字。
因此StringBuffer的性能要远低于StringBuilder。 5.3 应用场景 在字符串内容不经常发生变化的业务场景优先使用String类。
例如常量声明、少量的字符串拼接操作等。如果有大量的字符串内容拼接避免使用String与String之间的“”操作因为这样会产生大量无用的中间对象耗费空间且执行效率低下新建对象、回收对象花费大量时间 在频繁进行字符串的运算如拼接、替换、删除等并且运行在多线程环境下
5.3.1 使用StringBuffer例如HTTP参数解析与封装
5.3.1.1 多线程环境下使用同步的 StringBuffer 进行字符串拼接的例子
创建了两个线程 thread1 和 thread2 分别向 StringBuffer 对象 stringBuffer 中追加字符 A 和 B每个线程追加1000次。由于 StringBuffer 是线程安全的我们使用 synchronized 关键字对 stringBuffer 进行同步处理确保每个线程在访问和修改 stringBuffer 时的互斥性。
通过启动两个线程并等待它们执行完成最后输出 stringBuffer 的长度可以观察到最终拼接的字符串长度为 2000证明在多线程环境下使用同步的 StringBuffer 进行字符串拼接是安全和可靠的。
public class ThreadSafeStringBufferExample {public static void main(String[] args) {final StringBuffer stringBuffer new StringBuffer();// 创建两个线程进行字符串拼接Thread thread1 new Thread(() - {for (int i 0; i 1000; i) {synchronized (stringBuffer) {stringBuffer.append(A);}}});Thread thread2 new Thread(() - {for (int i 0; i 1000; i) {synchronized (stringBuffer) {stringBuffer.append(B);}}});// 启动线程thread1.start();thread2.start();// 等待线程执行完成try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}// 输出最终字符串长度System.out.println(Final string length: stringBuffer.length());}
}在频繁进行字符串的运算如拼接、替换、删除等并且运行在单线程环境下
建议使用StringBuilder例如SQL语句拼装、JSON封装等。
SQL语句拼装和JSON封装建议使用非线程安全的 StringBuilder 类来进行字符串拼接操作。StringBuilder 的性能比线程安全的 StringBuffer 更好因为它不需要进行同步处理。
5.3.2 使用 StringBuilder 进行SQL语句拼装和JSON封装的示例
5.3.2.1 SQL拼接
创建了一个 StringBuilder 对象 sql并使用连续的 append() 方法将 SQL 语句的各个部分拼接起来包括表名、列名和列值。最后通过调用 toString() 方法将 StringBuilder 转换为字符串并输出。
public class SQLStatementExample {public static void main(String[] args) {StringBuilder sql new StringBuilder();String tableName users;String columnName name;String columnValue John Doe;sql.append(SELECT * FROM ).append(tableName).append( WHERE ).append(columnName).append( ).append(columnValue).append();String query sql.toString();System.out.println(query);}
}输出
SELECT * FROM users WHERE name John Doe使用 StringBuilder 进行 SQL 语句的动态拼装。通过使用非线程安全的 StringBuilder可以避免多余的同步开销提高字符串拼接的效率和性能。
5.3.2.2 JSON封装
创建了一个 StringBuilder 对象 json用于存储 JSON 数据。然后使用 JSONObject 和 JSONArray 类来构建 JSON 结构并将其转换为字符串形式。最后通过调用 toString() 方法将 StringBuilder 转换为字符串并输出。
import org.json.JSONArray;
import org.json.JSONObject;public class JSONExample {public static void main(String[] args) {StringBuilder json new StringBuilder();// 创建一个 JSONObject 对象并设置键值对JSONObject person new JSONObject();person.put(name, John Doe);person.put(age, 30);person.put(isStudent, false);// 创建一个 JSONArray 对象并添加元素JSONArray hobbies new JSONArray();hobbies.put(reading);hobbies.put(coding);hobbies.put(gaming);// 将 JSONArray 添加到 JSONObject 中person.put(hobbies, hobbies);// 将 JSONObject 转换为字符串json.append(person.toString());String jsonString json.toString();System.out.println(jsonString);}
}输出
{name:John Doe,age:30,isStudent:false,hobbies:[reading,coding,gaming]}使用 StringBuilder 进行 JSON 数据的封装。通过使用非线程安全的 StringBuilder可以避免多余的同步开销提高字符串拼接的效率和性能。
请注意在多线程环境下进行字符串拼接操作时如果存在并发访问和修改的情况建议使用线程安全的 StringBuffer 或采用适当的同步机制来保证数据的一致性和线程安全性。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/83547.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!