java se好用吗_利用 Java SE 7 更好地管理资源

2011 年 5 月发布

作者:Julien Ponge

本文介绍 Java 7 针对自动资源管理问题给出的解决办法,即 Coin 项目中提出的新语言结构 try-with-resources 语句。

2e42e1f85a96a1c70ef8c80bffe5c1e6.gif:示例源文件 (zip)

简介

典型的 Java 应用程序可以处理多种类型的资源,如文件、流、套接字和数据库连接。必须谨慎处理这些资源,因为对它们的操作会占用系统资源。因此,需要确保即便在出错的情况下也能释放这些资源。实际上,不正确的资源管理是生产应用中常见的故障根源,常见的错误是,代码中其他位置出现异常后,数据库连接和文件描述符依然处于打开状态。由于操作系统和服务器应用程序通常有一个资源上限,因此在资源耗竭时这会导致应用服务器频繁重启。

针对 Java 中资源管理和异常管理的正确做法已经有了很好的文档说明。对于任何已成功初始化的资源,都需要相应地调用它的 close() 方法。这就要求严格遵守 try/catch/finally 块的用法,以确保任何从资源打开时起的执行路径最终都能调用一个方法来关闭资源。静态分析工具(如 FindBugs)在识别此类错误时很有帮助。然而通常的情况是,经验不足的开发人员和经验丰富的开发人员都会编写错误的资源管理代码,从而导致资源泄漏甚至更严重的后果。

然而,应该承认,编写正确的资源代码需要大量采用嵌套了 try/catch/finally 块的样板代码,您在后文中将看到这一点。正确编写这种代码本身很快就会成为难题。与此同时,Python 和 Ruby 等其他编程语言已经提供了语言级工具(即自动资源管理)来解决这一问题。

本文介绍 Java Platform, Standard Edition (Java SE) 7 针对自动资源管理问题给出的解决办法,即 Coin 项目中提出的新语言结构 try-with-resources 语句。我们将看到,该语句的好处远不止像 Java SE 5 的循环语句增强一样地加入更多语法糖。实际上,异常会彼此屏蔽,从而导致有时难以找到问题的根源。

本文首先将概述资源和异常管理,然后将从 Java 开发人员的视角介绍 try-with-resources 语句的要点。随后将展示如何准备一个类,使之支持此类语句。接下来,将讨论异常屏蔽的问题,以及 Java SE 7 做了哪些改变来解决此类问题。最后,本文将揭开语言扩展背后语法糖的神秘面纱,进行讨论并给出结论。

注意:本文所述示例的源代码可从这里下载:sources.zip

管理资源和异常

我们先从下面节选的一段代码开始: private void incorrectWriting() throws IOException {

DataOutputStream out = new DataOutputStream(new FileOutputStream("data"));

out.writeInt(666);

out.writeUTF("Hello");

out.close();

}

乍一看,此方法似乎不会造成什么损害:它打开一个名为 data 的文件,随后写入一个整数和一个字符串。java.io 程序包中对流类的设计使之能够通过修饰设计模式进行组合。

例如,我们可以在 DataOutputStream 与 FileOutputStream 之间添加一个用于压缩数据的输出流。关闭一个流时,也会关闭它所修饰的流。重新回到这个示例,在对 DataOutputStream 的实例调用 close() 时,同样也会调用 FileOutputStream 的 close() 方法。

然而,关于在这种方法中对 close() 方法的调用存在一个严重的问题。假设在写入整数或字符串时因底层文件系统已满而抛出一个异常。那么,将不再有机会调用 close() 方法。

这对于 DataOutputStream 不是什么严重的问题,因为它仅对 OutputStream 实例进行操作,用于将基本数据类型解码并把它们写入字节数组中。真正的问题在于 FileOutputStream,因为它在一个文件描述符内部保留了一个操作系统资源,仅在调用 close() 时才能释放该资源。因此,这种方法会泄漏资源。

这个问题对短时运行的程序基本无碍,但对于建立在 Java Platform, Enterprise Edition (Java EE) 应用服务器上的、长期运行的应用程序来说,由于达到了底层操作系统所允许打开的文件描述符的最大数量,可能会导致整个服务器重启。

一种正确地重写前述方法的方式如下: private void correctWriting() throws IOException {

DataOutputStream out = null;

try {

out = new DataOutputStream(new FileOutputStream("data"));

out.writeInt(666);

out.writeUTF("Hello");

} finally {

if (out != null) {

out.close();

}

}

}

在任何情况下,抛出的异常都会传播给方法的调用者,但 try 块后的 finally 块能确保调用数据输出流的 close() 方法。这相应地确保了底层文件输出流的 close() 方法同样获得调用,从而正确释放与文件关联的操作系统资源。

适合缺乏耐心者的 try-with-resources 语句

不可否认,前例中存在大量确保正确关闭资源的样板代码。如果存在更多的流、网络套接字或 Java 数据库连接 (JDBC) 连接,此类样板代码会使您更难以阅读一个方法的业务逻辑。更糟糕的是,它需要开发人员的自律,因为在编写错误处理和资源关闭逻辑时非常容易出错。

与此同时,其他编程语言已经引入了简化此类情况处理的结构。例如,上一个方法可以使用 Ruby 写成如下所示的样子: def writing_in_ruby

File.open('rdata', 'w') do |f|

f.write(666)

f.write("Hello")

end

end

用 Python 可写成这个样子: def writing_in_python():

with open("pdata", "w") as f:

f.write(str(666))

f.write("Hello")

在 Ruby 中,File.open 执行了一个代码块,即便在该块的执行出现异常时也能确保关闭所打开的文件。

Python 的示例与之相似,其特殊的 with 语句采用一个带有 close 方法和一个代码块的对象。同样,无论是否抛出异常,都能确保正确关闭资源。

Java SE 7 在 Coin 项目中引入了类似的语言结构。之前的示例可重写为如下所示: private void writingWithARM() throws IOException {

try (DataOutputStream out

= new DataOutputStream(new FileOutputStream("data"))) {

out.writeInt(666);

out.writeUTF("Hello");

}

}

新结构扩展了 try 块,按照与 for 循环相似的方式声明了资源。在 try 块中声明打开的任何资源都会关闭。因此,这个新结构使您不必配对使用 try 块与对应的 finally 块,后者专用于正确的资源管理。使用分号分隔每个资源,例如: try (

FileOutputStream out = new FileOutputStream("output");

FileInputStream  in1 = new FileInputStream(“input1”);

FileInputStream  in2 = new FileInputStream(“input2”)

) {

// Do something useful with those 3 streams!

}   // out, in1 and in2 will be closed in any case

最后需要提到的是,这样一条 try-with-resources 语句后面可能跟 catch 和 finally 块,就像 Java SE 7 之前的常规 try 语句一样。

构造可自动关闭的类

您可能已经猜到了,try-with-resources 语句无法管理所有类。Java SE 7 引入了一个新接口 java.lang.AutoCloseable。它的作用就是提供一个名为 close() 的 void 方法,该方法可能抛出一个检查到的异常 (java.lang.Exception)。任何希望在 try-with-resources 语句中使用的类都应实现该接口。强烈建议,实现的类和子接口应声明一种比 java.lang.Exception 更精确的异常类型,当然,更好的情况是,如果调用 close() 方法不会导致失败,就根本不用声明异常类型。

此类 close() 方法已经进行了改进,包含在标准 Java SE 运行时环境的许多类中,这些类包括 java.io、java.nio、javax.crypto、java.security、java.util.zip、java.util.jar、javax.net 和 java.sql packages。这种方法的主要优点在于,现有代码可继续像以前那样工作,而新代码可以轻松利用 try-with-resources 语句。

我们来看看以下示例: public class AutoClose implements AutoCloseable {

@Override

public void close() {

System.out.println(">>> close()");

throw new RuntimeException("Exception in close()");

}

public void work() throws MyException {

System.out.println(">>> work()");

throw new MyException("Exception in work()");

}

public static void main(String[] args) {

try (AutoClose autoClose = new AutoClose()) {

autoClose.work();

} catch (MyException e) {

e.printStackTrace();

}

}

} class MyException extends Exception {

public MyException() {

super();

}

public MyException(String message) {

super(message);

}

}

AutoClose 类实现了 AutoCloseable,因此可用作 try-with-resources 语句的一部分,如 main() 方法中所示。我们特意添加了一些控制台输出,并在该类的 work() 和 close() 方法中抛出异常。运行该程序将产生以下输出: >>> work()

>>> close()

MyException: Exception in work()

at AutoClose.work(AutoClose.java:11)

at AutoClose.main(AutoClose.java:16)

Suppressed: java.lang.RuntimeException: Exception in close()

at AutoClose.close(AutoClose.java:6)

at AutoClose.main(AutoClose.java:17)

输出显然证实了在进入应处理异常的 catch 块之前,确实调用了 close()。然而,Java 开发人员意外地发现,在 Java SE 7 中出现了以“Suppressed:(…)”为前缀的异常堆栈跟踪行。它相当于 close() 方法抛出的异常,但在 Java SE 7 之前,您可能从未遇到过这种形式的堆栈跟踪。这是怎么回事?

异常屏蔽

为了理解前面示例中所发生的情况,让我们暂时抛开 try-with-resources 语句,手动重新编写正确的资源管理代码。首先,我们提取将由 main 方法调用的以下静态方法: public static void runWithMasking() throws MyException {

AutoClose autoClose = new AutoClose();

try {

autoClose.work();

} finally {

autoClose.close();

}

}

随后,相应地改造 main 方法: public static void main(String[] args) {

try {

runWithMasking();

} catch (Throwable t) {

t.printStackTrace();

}

}

现在,运行程序后会给出以下输出: >>> work()

>>> close()

java.lang.RuntimeException: Exception in close()

at AutoClose.close(AutoClose.java:6)

at AutoClose.runWithMasking(AutoClose.java:19)

at AutoClose.main(AutoClose.java:52)

这段代码是在 Java SE 7 之前惯用的正确资源管理方法,它显示了一个异常被另一个异常屏蔽的问题。实际上,调用 runWithMasking() 方法的客户端代码将获知 close() 方法抛出一个异常,尽管实际上是 work() 方法先抛出了异常。

然而,一次只能抛出一个异常,这就意味着在处理异常时即使正确的代码也会遗漏一些信息。如果一个重要异常被关闭资源时进而抛出的另一个异常所屏蔽,开发人员就要浪费大量时间进行调试。敏锐的读者可能会对此提出异议,毕竟异常是可以嵌套的。然而,仅应对彼此之间存在因果关系的异常使用嵌套,通常将一个低级异常包装在位于应用程序架构较高层的异常中。一个很好的例子是 JDBC 驱动程序将套接字异常包装在一个 JDBC 连接中。我们的示例中实际上有两个异常:一个在 work() 中,一个在 close() 中,两者之间绝对不存在因果关系。

支持“被抑制的”异常

由于异常屏蔽在实际中是如此重要的一个问题,因此 Java SE 7 扩展了异常,这样就可以将“被抑制的”异常附加到主异常上。我们之前所说的“屏蔽的”异常实际上就是一个被抑制并附加到主异常的异常。

java.lang.Throwable 的扩展如下: public final void addSuppressed(Throwable exception) 将一个被抑制的异常附加到另一个异常上,从而避免异常屏蔽。

public final Throwable[] getSuppressed() 获取添加到一个异常中的被抑制的异常。

这些扩展是专门为支持 try-with-resources 语句和修复异常屏蔽问题而引入的。

回到之前的 runWithMasking() 方法,我们在考虑支持被抑制异常的前提下重新编写此方法: public static void runWithoutMasking() throws MyException {

AutoClose autoClose = new AutoClose();

MyException myException = null;

try {

autoClose.work();

} catch (MyException e) {

myException = e;

throw e;

} finally {

if (myException != null) {

try {

autoClose.close();

} catch (Throwable t) {

myException.addSuppressed(t);

}

} else {

autoClose.close();

}

}

}

很明显,这里使用了大量代码,其目的仅仅是正确处理一个可自动关闭类的两个异常抛出方法!一个局部变量用于捕获主异常,也就是 work() 方法可能抛出的异常。如果抛出这样一个异常,则捕获该异常,随后立即再次抛出,以便将其余工作委托给 finally 块。

进入 finally 块,检查对主异常的引用。如果抛出了一个异常,则 close() 方法可能抛出的异常将作为被抑制异常附加到此异常。否则将调用 close() 方法,如果该方法抛出一个异常,那么该异常实际上就是主异常,因此不会屏蔽其他异常。

我们来运行使用这个新方法修改后的程序: >>> work()

>>> close()

MyException: Exception in work()

at AutoClose.work(AutoClose.java:11)

at AutoClose.runWithoutMasking(AutoClose.java:27)

at AutoClose.main(AutoClose.java:58)

Suppressed: java.lang.RuntimeException: Exception in close()

at AutoClose.close(AutoClose.java:6)

at AutoClose.runWithoutMasking(AutoClose.java:34)

... 1 more

正如您所见到的那样,我们手动重现了前文所述的 try-with-resources 语句的行为。

语法糖揭秘

我们实现的 runWithoutMasking() 方法通过正确关闭资源以及防止异常屏蔽来重现了 try-with-resources 语句的行为。实际上,Java 编译器将以下方法的代码扩展为与 runWithoutMasking() 代码一致的情形,使用了 try-with-resources 语句: public static void runInARM() throws MyException {

try (AutoClose autoClose = new AutoClose()) {

autoClose.work();

}

}

可以通过反编译来进行检查。虽然我们可以使用 Java Development Kit (JDK) 二进制工具中包含的 javap 来比较字节码,但我们把它当作一个字节码到 Java 源代码的反编译器来使用。JD-GUI 工具提取出的 runInARM() 代码如下(经过重新排版): public static void runInARM() throws MyException {

AutoClose localAutoClose = new AutoClose();

Object localObject1 = null;

try {

localAutoClose.work();

} catch (Throwable localThrowable2) {

localObject1 = localThrowable2;

throw localThrowable2;

} finally {

if (localAutoClose != null) {

if (localObject1 != null) {

try {

localAutoClose.close();

} catch (Throwable localThrowable3) {

localObject1.addSuppressed(localThrowable3);

}

} else {

localAutoClose.close();

}

}

}

}

可以看到,我们手动编写的代码使用的资源管理画布与编译器根据 try-with-resources 语句推断出的画布相同。还应注意到,编译器处理了可能为 null 的资源引用,在 finally 块中添加了额外的 if 语句来检查给定资源是否为 null,从而避免对 null 引用调用 close() 时的空指针异常。我们的手动实现中并未这样做,因为资源不可能为 null。但编译器会系统性地生成此类代码。

现在,我们来考虑另外一个示例,这次涉及三个资源: private static void compress(String input, String output) throws IOException {

try(

FileInputStream fin = new FileInputStream(input);

FileOutputStream fout = new FileOutputStream(output);

GZIPOutputStream out = new GZIPOutputStream(fout)

) {

byte[] buffer = new byte[4096];

int nread = 0;

while ((nread = fin.read(buffer)) != -1) {

out.write(buffer, 0, nread);

}

}

}

这个方法操纵三个资源来压缩一个文件:一个流用于读取、一个流用于压缩、一个流指向输出文件。从资源管理的视角来看,这样的代码是正确的。在 Java SE 7 之前,您不得不类似于下面的代码,这段代码是再次使用 JD-GUI 来反编译包含此方法的类获得的: private static void compress(String paramString1, String paramString2)

throws IOException {

FileInputStream localFileInputStream = new FileInputStream(paramString1); Object localObject1 = null;

try {

FileOutputStream localFileOutputStream = new FileOutputStream(paramString2); Object localObject2 = null;

try {

GZIPOutputStream localGZIPOutputStream = new GZIPOutputStream(localFileOutputStream); Object localObject3 = null;

try {

byte[] arrayOfByte = new byte[4096];

int i = 0;

while ((i = localFileInputStream.read(arrayOfByte)) != -1) {

localGZIPOutputStream.write(arrayOfByte, 0, i);

}

} catch (Throwable localThrowable6) {

localObject3 = localThrowable6;

throw localThrowable6;

} finally {

if (localGZIPOutputStream != null) {

if (localObject3 != null) {

try {

localGZIPOutputStream.close();

} catch (Throwable localThrowable7) {

localObject3.addSuppressed(localThrowable7);

}

} else {

localGZIPOutputStream.close();

}

}

}

} catch (Throwable localThrowable4) {

localObject2 = localThrowable4;

throw localThrowable4;

} finally {

if (localFileOutputStream != null) {

if (localObject2 != null) {

try {

localFileOutputStream.close();

} catch (Throwable localThrowable8) {

localObject2.addSuppressed(localThrowable8);

}

} else {

localFileOutputStream.close();

}

}

}

} catch (Throwable localThrowable2) {

localObject1 = localThrowable2;

throw localThrowable2;

} finally {

if (localFileInputStream != null) {

if (localObject1 != null) {

try {

localFileInputStream.close();

} catch (Throwable localThrowable9) {

localObject1.addSuppressed(localThrowable9);

}

} else {

localFileInputStream.close();

}

}

}

}

对于这样的示例来说,Java SE 7 中的 try-with-resources 语句的好处是不言而喻的:要编写的代码很少、代码的可读性更高,最后但并非最不重要的是,代码不会泄漏资源!

讨论

java.lang.AutoCloseable 接口中 close() 方法的定义意味着可能抛出 java.lang.Exception。然而,前面的 AutoClose 示例对该方法进行声明,但并未提及任何检查到的异常,这是我们有意为之,部分是为了说明异常屏蔽。

可自动关闭类的规范建议避免抛出 java.lang.Exception,优先使用具体的受检异常,如果预计 close() 方法不会失败,就不必提及任何受检异常。此外还建议,不要声明任何不应被抑制的异常,java.lang.InterruptedException 就是最好的例子。实际上,抑制该异常并将其附加到另一个异常可能会导致忽略线程中断事件,使应用程序处于不一致的状态。

一个关于 try-with-resources 语句使用的合理问题是,与手动编写的正确资源管理代码相比,其对性能的影响如何。实际上并不存在性能方面的影响,因为编译器为所有异常的正确处理推断出尽可能少的正确代码,正如我们在之前示例中通过反编译所演示的那样。

最后要说的是,try-with-resources 语句是语法糖,就像 Java SE 5 为扩展迭代器循环而引入的增强 for 循环一样。

话虽如此,我们仍然可以限制 try-with-resources 语句扩展的复杂程度。一般来说,一个 try 块声明的资源越多,所生成的代码也就越复杂。之前的 compress() 方法可重写成仅使用两个资源而不是三个,从而生成更精简的异常处理块: private static void compress(String input, String output) throws IOException {

try(

FileInputStream fin = new FileInputStream(input);

GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(output))

) {

byte[] buffer = new byte[4096];

int nread = 0;

while ((nread = fin.read(buffer)) != -1) {

out.write(buffer, 0, nread);

}

}

}

就像 Java 中出现 try-with-resources 语句之前的情况一样,一般经验是,开发人员在链接资源实例化时应始终明白需要取舍的东西。为此,最好的方法就是阅读每个资源的 close() 方法的规范,理解其语义和影响。

回到本文最初的 writingWithARM() 示例,链接是安全的,因为 DataOutputStream 不可能在 close() 上抛出异常。但是,这不适用于最后一个示例,因为 GZIPOutputStream 会尝试写入其余压缩数据作为 close() 方法的一部分。如果在写入压缩文件时,抛出异常的时间较早,GZIPOutputStream 中的 close() 方法更有可能进而抛出另一个异常,导致不会调用 FileOutputStream 中的 close() 方法,从而泄漏一个文件描述符资源。

好的做法是在 try-with-resources 语句中为每一个持有关键系统资源(如文件描述符、套接字或者 JDBC 连接)的每个资源进行单独声明,必须确保 close() 方法最终得到调用。否则,如果相关资源 API 允许,选择链接分配就不仅是一种惯例:在防止资源泄漏的同时还能得到更为紧凑的代码。

结论

本文介绍了 Java SE 7 中一种新的用于安全管理资源的语言结构。这种扩展带来的影响不仅仅是更多的语法糖。事实上,它能位开发人员生成了正确的代码,消除了编写容易出错的样板代码的需要。更重要的是,这种变化还伴随着将一个异常附加到另一个异常的改进,从而为众所周知的异常彼此屏蔽问题提供了完善的解决方案。

另请参见

下面是其他一些资源:

关于作者

Julien Ponge 是一位长期从事开源工作的技术高人。他创建了 IzPack 安装程序框架,还参与了其他几个项目,包括与 Sun Microsystems 合作的 GlassFish 应用服务器。他拥有 UNSW Sydney 和 UBP Clermont-Ferrand 的计算机科学博士学位,目前是 INSA de Lyon 计算机科学与工系程的副教授,并且是 INRIA Amazones 团队的一名研究人员。由于熟练掌握行业和学术两个领域中的语言,因此他正在积极推进这两个领域之间更进一步的协作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432450.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

法线贴图Nomal mapping 原理

法线贴图多用在CG动画的渲染以及游戏画面的制作上,将具有高细节的模型通过映射烘焙出法线贴图,贴在低端模型的法线贴图通道上,使之拥有法线贴图的渲染效果,却可以大大降低渲染时需要的面数和计算内容,从而达到优化动画…

Javascript引擎单线程机制及setTimeout执行原理说明

setTimeout用法在实际项目中还是会时常遇到。比如浏览器会聪明的等到一个函数堆栈结束后才改变DOM,如果再这个函数堆栈中把页面背景先从白色设为红色,再设回白色,那么浏览器会认为DOM没有发生任何改变而忽略这两句话,因此我们可以…

解决VS命令提示符 “Setting environment for using Microsoft Visual Studio. 此时不应有“系列错误

一、起因 最近在玩Boost库。当然首先是要进行Boost库的安装和配置。于是浅墨Google了一下boost库的安装配置攻略,下载了最新版1.55的boost库,就愉悦地开始进行配置了。 当进行到第五步,要在VS命令提示符中运行bootstrap.bat的时候&#xff0c…

yii2多语言设置

yii2的多语言切换功能 1.页面添加语言切换按钮&#xff0c;如下图&#xff1a; 代码如下&#xff1a; <ul> <li> <a href"javascript:;" οnclick"changeLanguage(en_US);"> <span><?php echo …

APP安全环节缺失,手游运营商怎样应对APP破解困境

2013年手游行业的规模与收入均实现了大幅增长&#xff0c;发展势头强劲。然而&#xff0c;在手游快速发展的同一时候&#xff0c;因为监管、审核等方面存在着漏洞&#xff0c;手机游戏软件被破解后注入恶意代码、盗取用户財产、窃取用户设备信息的现象屡见不鲜。手游被破解后黑…

linux php cpu,获取Linux服务器性能CPU、内存、硬盘等使用率 PHP

数据库配置文件&#xff1a; conn.phpdefine("MONITORED_IP", "172.16.0.191"); //被监控的服务器IP地址 也就是本机地址define("DB_SERVER", "172.16.7.2"); //存放数据的服务器IP地址define("DB_USER", "roo…

信息论与编码matlab实验报告,信息论与编码实验程序与结果图(matlab).doc

信息论与编码实验程序与结果图(matlab).doc 1信源熵实验程序&#xff1a;clc;closeall;clear;linwidd1fontt20p00;pd1;N20plinspace(p0,pd,N);I-log2(p);plot(p,I, k );title( I-log2(p)函数图 );xlabel( p );ylabel( I );clc;closeall;clear;linwidd1fontt20p00;pd1;N20plinsp…

OpenGL: 实现立体显示

立体显示原理&#xff1a;设没有立体显示的模型视图矩阵ModelView为Mv,投影矩阵为Mp&#xff0c;则、物体空间的任何一点为P&#xff0c;则变换到屏幕坐标P*MpMvP&#xff1b;注意前面已经说过opengl里面坐标列优先&#xff0c;所以矩阵都是右乘。 左眼和右眼的变换都是由中间的…

数学 之 hdu 4861

// [7/23/2014 Sjm] /* 对于此题&#xff0c;举出数据找规律&#xff0c;即可AC。。。 不过悲催的WA了好多次&#xff0c;后来发现竟把"YES"打印成"Yes"了。。。。 注释掉的代码是用来找规律的。。。 */ 1 #include <iostream>2 #include <cs…

centos 6 安装mysql,CentOS6.5安装MySQL教程(完整教程)

Step 1&#xff1a;检测系统是否安装MYSQL# yum list installed | grep mysql顺便提下如果yum有如下提示不能用的情况&#xff1a;yum在自动更新原因是yum在自动更新 只要关掉它就OK了解决方案&#xff1a;直接输入# rm -f /var/run/yum.pid或者&#xff1a;# /etc/init.d/y…

php通过条件来定义const,php用const出错是什么原因

大家都知道define是定义常量的,如果在类中定义常量呢&#xff1f;当然不能用define&#xff0c;而用const&#xff0c;如下例&#xff1a;<?php //在类外面通常这样定义常量define("PHP","phpernote.com");class MyClass{ //常量的值将始终保持不变。在…

UE4角色Location远距离时动画抖动问题(float精度不够)解决方案

正题&#xff1a;关于UE4引擎当角色Location超过9999.999后&#xff0c;角色动画更新抖动问题的解决思路。 前提&#xff1a; 1.UE4引擎中距离单位是厘米(cm)&#xff0c;也就说我们制作好1.8米的角色在UE4中为180个虚幻单位。这样做个人愚见是为了提高浮点值&#xff08;float…

android Handler的使用(一)

Handler的使用(一) Handler基本概念&#xff1a; Handler主要用于异步消息的处理&#xff1a;当发出一个消息之后&#xff0c;首先进入一个消息队列&#xff0c;发送消息的函数即刻返回&#xff0c;而另外一个部分逐个的在消息队列中将消息取出&#xff0c;然后对消息进行出来…

现实地形导入UE4全流程

制作地形方法很多&#xff0c;今天给大家分享一种原创野套路。此方法特点是将现实中的地形于UE4中呈现&#xff0c;而不是手动绘制地形。首先从地理空间数据云获得指定区域的地理数据&#xff0c;然后使用GlobalMapper更准确选出区域并把数据转换成WorldMachine可识别的格式&am…

大地形pawn抖动问题

在pawn的event tick里调用下面函数即可

Bootstrap页面布局16 - BS导航菜单和其响应式布局以及导航中的下拉菜单

代码&#xff1a; <div classcontainer-fluid><h2 classpage-header>导航</h2><!--    .navrbar navbar-fixed-top:导航固定显示在顶部&#xff0c;对应的navbar-fixed-bottom:导航固定显示在页面底部    .brand:提示文字或者主题    .active…

hdu 1874(Dijkstra + Floyd)

链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1874 畅通工程续 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 27692 Accepted Submission(s): 10019 Problem Description某省自从实行了很多年…

Web 开发中很实用的10个效果【附源码下载】

在工作中&#xff0c;我们可能会用到各种交互效果。而这些效果在平常翻看文章的时候碰到很多&#xff0c;但是一时半会又想不起来在哪&#xff0c;所以养成知识整理的习惯是很有必要的。这篇文章给大家推荐10个在 Web 开发中很有用的效果&#xff0c;记得收藏&#xff01; 超炫…

蓝图中实现人物移动2

从Pawn继承一个蓝图类并编写下面代码&#xff0c;并添加摄像机组件 1. 实现前后移动2. 实现左右移动3. 实现鼠标移动方向4. 实现鼠标俯仰