原文:>
你这个问题的解决
问题定位:
在堆栈异常信息的第一行就可以定位到是哪里出了空指针,倘若这里不是你写的类,可以往下翻一下,找到你写的类,就是这里出现的空指针。
问题解决:
对一个空对象调用里面的方法或者属性的时候会报空指针,检查这个对象为什么是空即可。
Java 空指针异常的若干解决方案
Java 中任何对象都有可能为空,当我们调用空对象的方法时就会抛出 NullPointerException 空指针异常,这是一种非常常见的错误类型。我们可以使用若干种方法来避免产生这类异常,使得我们的代码更为健壮。本文将列举这些解决方案,包括传统的空值检测、编程规范、以及使用现代 Java 语言引入的各类工具来作为辅助。
运行时检测
最显而易见的方法就是使用 if (obj == null) 来对所有需要用到的对象来进行检测,包括函数参数、返回值、以及类实例的成员变量。当你检测到 null 值时,可以选择抛出更具针对性的异常类型,如 IllegalArgumentException,并添加消息内容。我们可以使用一些库函数来简化代码,如 Java 7 开始提供的 Objects#requireNonNull 方法:
public void testObjects(Object arg) {
Object checked = ObjectsrequireNonNull(arg, "arg must not be null");
checkedtoString();}
Guava 的 Preconditions 类中也提供了一系列用于检测参数合法性的工具函数,其中就包含空值检测:
public void testGuava(Object arg) {
Object checked = PreconditionscheckNotNull(arg, "%s must not be null", "arg");
checkedtoString();
}
我们还可以使用 Lombok 来生成空值检测代码,并抛出带有提示信息的空指针异常:
public void testLombok(@NonNull Object arg) {
argtoString();
生成的代码如下:
public void testLombokGenerated(Object arg) {
if (arg == null) {
throw new NullPointerException("arg is marked @NonNull but is null");
}
argtoString();
}
这个注解还可以用在类实例的成员变量上,所有的赋值 *** 作会自动进行空值检测。
编程规范
·通过遵守某些编程规范,也可以从一定程度上减少空指针异常的发生。
使用那些已经对 null 值做过判断的方法,如 String#equals、String#valueOf、以及三方库中用来判断字符串和集合是否为空的函数:
if (str != null && strequals("text")) {}
if ("text"equals(str)) {}
if (obj != null) { objtoString(); }
StringvalueOf(obj); // "null"
// from spring-core
StringUtilsisEmpty(str);
CollectionUtilsisEmpty(col);
// from guava
StringsisNullOrEmpty(str);
// from commons-collections4
CollectionUtilsisEmpty(col);
·如果函数的某个参数可以接收 null 值,考虑改写成两个函数,使用不同的函数签名,这样就可以强制要求每个参数都不为空了:
public void methodA(Object arg1) {
methodB(arg1, new Object[0]);
}
public void methodB(Object arg1, Object[] arg2) {
for (Object obj : arg2) {} // no null check
}
·如果函数的返回值是集合类型,当结果为空时,不要返回 null 值,而是返回一个空的集合;如果返回值类型是对象,则可以选择抛出异常。Spring JdbcTemplate 正是使用了这种处理方式:
// 当查询结果为空时,返回 new ArrayList<>()
jdbcTemplatequeryForList("SELECT FROM person");
// 若找不到该条记录,则抛出 EmptyResultDataAccessException
jdbcTemplatequeryForObject("SELECT age FROM person WHERE id = 1", Integerclass);
// 支持泛型集合
public <T> List<T> testReturnCollection() {
return CollectionsemptyList();
}
静态代码分析
Java 语言有许多静态代码分析工具,如 Eclipse IDE、SpotBugs、Checker Framework 等,它们可以帮助程序员检测出编译期的错误。结合 @Nullable 和 @Nonnull 等注解,我们就可以在程序运行之前发现可能抛出空指针异常的代码。
但是,空值检测注解还没有得到标准化。虽然 2006 年 9 月社区提出了 JSR 305 规范,但它长期处于搁置状态。很多第三方库提供了类似的注解,且得到了不同工具的支持,其中使用较多的有:
javaxannotationNonnull:由 JSR 305 提出,其参考实现为 comgooglecodefindbugsjsr305;
orgeclipsejdtannotationNonNull:Eclipse IDE 原生支持的空值检测注解;
eduumdcsfindbugsannotationsNonNull:SpotBugs 使用的注解,基于 findbugsjsr305;
orgspringframeworklangNonNull:Spring Framework 50 开始提供;
orgcheckerframeworkcheckernullnessqualNonNull:Checker Framework 使用;
androidsupportannotationNonNull:集成在安卓开发工具中;
我建议使用一种跨 IDE 的解决方案,如 SpotBugs 或 Checker Framework,它们都能和 Maven 结合得很好。
SpotBugs 与 @NonNull、@CheckForNull
SpotBugs 是 FindBugs 的后继者。通过在方法的参数和返回值上添加 @NonNull 和 @CheckForNull 注解,SpotBugs 可以帮助我们进行编译期的空值检测。需要注意的是,SpotBugs 不支持 @Nullable 注解,必须用 @CheckForNull 代替。如官方文档中所说,仅当需要覆盖 @ParametersAreNonnullByDefault 时才会用到 @Nullable。
官方文档 中说明了如何将 SpotBugs 应用到 Maven 和 Eclipse 中去。我们还需要将 spotbugs-annotations 加入到项目依赖中,以便使用对应的注解。
<dependency><groupId>comgithubspotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
<version>317</version>
</dependency>
以下是对不同使用场景的说明:
@NonNullprivate Object returnNonNull() {
// 错误:returnNonNull() 可能返回空值,但其已声明为 @Nonnull
return null;
}
@CheckForNull
private Object returnNullable() {
return null;
}
public void testReturnNullable() {
Object obj = returnNullable();
// 错误:方法的返回值可能为空
Systemoutprintln(objtoString());
}
private void argumentNonNull(@NonNull Object arg) {
Systemoutprintln(argtoString());
}
public void testArgumentNonNull() {
// 错误:不能将 null 传递给非空参数
argumentNonNull(null);
}
public void testNullableArgument(@CheckForNull Object arg) {
// 错误:参数可能为空
Systemoutprintln(argtoString());
}
对于 Eclipse 用户,还可以使用 IDE 内置的空值检测工具,只需将默认的注解 orgeclipsejdtannotationNullable 替换为 SpotBugs 的注解即可:
Checker Framework 与 @NonNull、@Nullable
Checker Framework 能够作为 javac 编译器的插件运行,对代码中的数据类型进行检测,预防各类问题。我们可以参照 官方文档,将 Checker Framework 与 maven-compiler-plugin 结合,之后每次执行 mvn compile 时就会进行检查。Checker Framework 的空值检测程序支持几乎所有的注解,包括 JSR 305、Eclipse、甚至 lombokNonNull。
import orgcheckerframeworkcheckernullnessqualNullable;@Nullable
private Object returnNullable() {
return null;
}
public void testReturnNullable() {
Object obj = returnNullable();
// 错误:obj 可能为空
Systemoutprintln(objtoString());
}
Checker Framework 默认会将 @NonNull 应用到所有的函数参数和返回值上,因此,即使不添加这个注解,以下程序也是无法编译通过的:
private Object returnNonNull() {// 错误:方法声明为 @NonNull,但返回的是 null。
return null;
}
private void argumentNonNull(Object arg) {
Systemoutprintln(argtoString());
}
public void testArgumentNonNull() {
// 错误:参数声明为 @NonNull,但传入的是 null。
argumentNonNull(null);
}
Checker Framework 对使用 Spring Framework 50 以上的用户非常有用,因为 Spring 提供了内置的空值检测注解,且能够被 Checker Framework 支持。一方面我们无需再引入额外的 Jar 包,更重要的是 Spring Framework 代码本身就使用了这些注解,这样我们在调用它的 API 时就能有效地处理空值了。举例来说,StringUtils 类里可以传入空值的函数、以及会返回空值的函数都添加了 @Nullable 注解,而未添加的方法则继承了整个框架的 @NonNull 注解,因此,下列代码中的空指针异常就可以被 Checker Framework 检测到了:
// 这是 spring-core 中定义的类和方法public abstract class StringUtils {
// str 参数继承了全局的 @NonNull 注解
public static String capitalize(String str) {}
@Nullable
public static String getFilename(@Nullable String path) {}
}
// 错误:参数声明为 @NonNull,但传入的是 null。
StringUtilscapitalize(null);
String filename = StringUtilsgetFilename("/path/to/file");
// 错误:filename 可能为空。
Systemoutprintln(filenamelength());
Optional 类型
Java 8 引入了 Optional<T> 类型,我们可以用它来对函数的返回值进行包装。这种方式的优点是可以明确定义该方法是有可能返回空值的,因此调用方必须做好相应处理,这样也就不会引发空指针异常。但是,也不可避免地需要编写更多代码,而且会产生很多垃圾对象,增加 GC 的压力,因此在使用时需要酌情考虑。
Optional<String> opt;// 创建
opt = Optionalempty();
opt = Optionalof("text");
opt = OptionalofNullable(null);
// 判断并读取
if (optisPresent()) {
optget();
}
// 默认值
optorElse("default");
optorElseGet(() -> "default");
optorElseThrow(() -> new NullPointerException());
// 相关 *** 作
optifPresent(value -> {
Systemoutprintln(value);
});
optfilter(value -> valuelength() > 5);
optmap(value -> valuetrim());
optflatMap(value -> {
String trimmed = valuetrim();
return trimmedisEmpty() Optionalempty() : Optionalof(trimmed);
});
方法的链式调用很容易引发空指针异常,但如果返回值都用 Optional 包装起来,就可以用 flatMap 方法来实现安全的链式调用了:
String zipCode = getUser()flatMap(User::getAddress)
flatMap(Address::getZipCode)
orElse("");
Java 8 Stream API 同样使用了 Optional 作为返回类型:
stringListstream()findFirst()orElse("default");stringListstream()
max(ComparatornaturalOrder())
ifPresent(Systemout::println);
此外,Java 8 还针对基础类型提供了单独的 Optional 类,如 OptionalInt、OptionalDouble 等,在性能要求比较高的场景下很适用。
其它 JVM 语言中的空指针异常
Scala 语言中的 Option 类可以对标 Java 8 的 Optional。它有两个子类型,Some 表示有值,None 表示空。
val opt: Option[String] = Some("text")optgetOrElse("default")
除了使用 Option#isEmpty 判断,还可以使用 Scala 的模式匹配:
opt match {case Some(text) => println(text)
case None => println("default")
Scala 的集合处理函数库非常强大,Option 则可直接作为集合进行 *** 作,如 filer、map、以及列表解析(for-comprehension):
optmap(_trim)filter(_length > 0)map(_toUpperCase)getOrElse("DEFAULT")val upper = for {
text <- opt
trimmed <- Some(texttrim())
upper <- Some(trimmed) if trimmedlength > 0
} yield upper
uppergetOrElse("DEFAULT")
Kotlin 使用了另一种方式,用户在定义变量时就需要明确区分 可空和不可空类型。当可空类型被使用时,就必须进行空值检测。
var a: String = "text"a = null // 错误:无法将 null 赋值给非空 String 类型。
val b: String = "text"
// 错误: *** 作可空类型时必须使用安全 *** 作符()或强制忽略(!!)。
println(blength)
val l: Int = blength // 安全 *** 作
b!!length // 强制忽略,可能引发空值异常
Kotlin 的特性之一是与 Java 的可互 *** 作性,但 Kotlin 编译器无法知晓 Java 类型是否为空,这就需要在 Java 代码中使用注解了,而 Kotlin 支持的 注解 也非常广泛。Spring Framework 50 起原生支持 Kotlin,其空值检测也是通过注解进行的,使得 Kotlin 可以安全地调用 Spring Framework 的所有 API。
结论
在以上这些方案中,我比较推荐使用注解来预防空指针异常,因为这种方式十分有效,对代码的侵入性也较小。所有的公共 API 都应该使用 @Nullable 和 @NonNull 进行注解,这样就能强制调用方对空指针异常进行预防,让我们的程序更为健壮。
年 月 日
Ruby on Rails 开发和 Java&# ; 开发有着本质的不同 在 跨越边界 系列的最后一期中 Bruce Tate 将概述使用 Rails 从头开发一个复杂 可伸缩的 Web 站点时所发现的二者的主要差异Rails 开发人员常常把 Java 开发人员看作是沉闷而劳碌的老古董 而 Java 崇拜者则常常认为 Ruby on Rails 只是一个玩具 根本不能用于任何严肃的软件开发 作为一名对这两种技术都有着广泛使用经验的顾问 我认为真实的情形介于这两种观点之间 由于跨越边界 系列文章即将结束 因此我打算对它们再作一次比较 本文并非考察某种特殊的技术或语言 而是主要介绍我当前正在从事的项目 并将它与我以前从事的 Java 项目进行比较 另外 我建议您阅读 跨越边界 系列的前几期文章 对相关主题作更深入的了解 这种直接的说明可让您在二者之间权衡利弊 并可能使您在数据库 Web 应用程序 green field 的开发中通过使用 Rails 获益
业务问题
关于本系列在 跨越边界 系列文章中 作者 Bruce Tate 提出这样一种观点 当今的 Java 程序员们可以通过学习其他方法和语言更好地武装自己 自从 Java 技术明显成为所有开发项目最好的选择以来 编程前景已经发生了改变 其他框架影响着 Java 框架的构建方式 从其他语言学到的概念也可以影响 Java 编程 您编写的 Python(或 Ruby Smalltalk 等语言)代码可以改变编写 Java 代码的方式 本系列介绍与 Java 开发完全不同的编程概念和技术 但是这些概念和技术也可以直接应用于 Java 开发 在某些情况下 需要集成这些技术来利用它们 在其他情况下 可以直接应用概念 具体的工具并不重要 重要的是其他语言和框架可以影响 Java 社区中的开发人员 框架 甚至是基本方式
不论是 Ruby on Rails 框架还是任何 Java 框架都不能解决所有问题 为了提高成功的几率 需要长期 细致地考察业务问题 理解周围的各种假定情况 以及了解您的团队 只有这样才能选出正确的语言来进行开发
去年 Arvato Systems 聘请我带领团队构建 Ch 它是一种新平台 用于将非赢利性团体和捐赠人组织在一起 与很多 Internet 公司一样 我们向客户展示了可购买的实际产品 与其他公司不同的是 这些 产品 指的是提供机会 比如 一名癌症研究员一小时服务收费 美元 帮助盲人收费 美元 或者保护一英亩雨林一个月收费 美元 我们面临两大挑战 一份很紧凑的时间表和长期的复杂性
开发工作从九月份开始 要求必须在十一月份之前建立起一个站点 以便有机会赶上圣诞节期间的通信量高峰 (最终我们超出了十一月份这个期限两星期 )在其他开发语言中 基于 Java 的解决方案可能要花费 到 个月才能完成这一任务 因此生产力是一个很重要的考虑因素 这与 Java 部署思想相悖
通过对竞争对手和项目的考察 我们了解到 我们希望能够每天给站点带来几百万次点击的通信量 而我们需要每天有几十万次成功的点击 因此可伸缩性也是一个考虑因素 这与 Java 部署思想相符
最后 我们了解到 发布初始站点只是一个开始 我们只实现了总体规划的百分之三 因此我们所选的技术需要根据复杂性和负载作出一些调整 我认为 Ruby 语言在复杂性方面会更易于调整 因为它提供了对更高级语言和特性(如开放类)的支持 以及具有更少配置需求和更简单 集成化编程模型的 Rails 框架
虽然我们面临着时间和可伸缩性方面的挑战 但是也拥有一些其他的有利因素 我们拥有一张完全空白的候选名单 可以选择想要的任何技术 任何团队 可以定义项目 培训和全部的技术 我们可完全自由地作出选择
Java 语言是一种优秀的通用语言 它总是应用于新的技术领域 如嵌入式系统和移动设备 Java 语言在广泛关注的集成方面也表现优秀 它具有高性能 流行并受到平台的良好支持 但是正如您在本系列中所了解到的那样 Java 语言并不是用于数据库支持的 Web 应用程序的 green field 开发的最佳选择
相比之下 Ruby on Rails 框架则是新的 并没有很多人使用 Rails 开发高通信流量的站点 并且几乎不存在什么使用 Rails 多年开发项目的经验 但它仍然是一种高生产力的数据库支持的 Web 应用程序开发的框架 最后 尽管我们对 Rails 缺乏开发长期项目的经验和并未得到广泛部署有所顾虑 但那份紧凑的时间表驱使我们选择了 Ruby on Rails
作出这个决定之后 我们发现招募项目人才非常容易 我们还发现早期的生产力优势非常显著 —— 甚至比预期的还要好 我们确实遇到了一些早期的稳定性问题 因此加强了测试工作的力度 此后稳定性得到了极大的改进
原理
每个框架设计者都使用一组假定来构造该框架的重写原理 学习遵守该原理的约束可使您愉快地进行编程 而挑战这些约束则会使您的编程受挫 Rails 框架和 Java 框架拥有很多不同的原理
Rails 是一种集成框架 需要使用高度利用 Ruby 语言的动态本质 Rails 开发人员强调框架的生产力特性而不是工具特性 并且常常将 Web 架构看得非常简单 在本系列的前几篇文章中您已了解到这一点 Java 设计者通常必须分块地组合开发环境 独立地选择持久性 Web 和集成层 他们通常严重地依赖工具来简化核心任务 Web 架构设计趋向于较为复杂
完全集成
Java 框架往往是解决一个小问题(比如持久性或查看组织) 而 Rails 则是一个集成环境 Rails 开发人员的优势在于不必解决与许多不同框架集成的问题 大多数 Hibernate 开发人员陷入了过早关闭与 Java Web 框架之间的连接的陷阱 Rails 视图框架是从头构建的 以便与 ActiveRecord 集成(Rails 持久性框架) 当您考察用于 Web 服务 配置和插件的 Rails 框架时也会发现类似的经验 Java 编程支持各种不同的框架 对于所有这些框架使用不同的集成策略
Java 开发人员的优势在于选择 如果您需要从头构建一个框架 则可能要考虑使用基于 SQL 的解决方案用于数据库集成(如 iBATIS 或 Java 编程中基于 JDBC 的包装框架之一) 反过来 如果要使用一种古老的模式进行编程 则可能要使用对象关系映射框架(如 Hibernate) 相比之下 如果您使用 Rails 则拥有一个主要选择 ActiveRecord 这意味着 Java 框架提供了更多的选择 有时能提供更好的集成开发项目的解决方案 但是由于我们要开发一个 green field 项目 因此选择算不上是一个问题
一种动态语言
Rails 原理的下一个主要部分是动态编程语言 Java 工具往往可以有效地使用 Java 类型模型提供的额外信息 工具可以识别错误和有效地重构代码 Rails 还可有效地利用编程语言的优点 Ruby 是一种构建特定于域的语言(DSL)的理想语言 Rails 集中使用 DSL 来完成从构建模型对象之间的关系到指定自定义组件(如状态机器或可上传的图像)的所有工作 动态语言常常更加简洁 因此 Rails 项目比 Java 项目要简练得多 可让用户更简练地表达代码和配置 在 Ch 项目中 我们发现技术顶尖的程序员可达到更高的生产力 但是我们确实需要招募经验更丰富的开发人员 我对这种妥协非常满意
传统的 Java 程序员对 IDE 有着近乎虔诚的热爱 造成这一现象有充分的理由 IDE 提供了语法的完整性检查 修正了小错误并提供了增量编译以便更快地完成编码 编译 部署和测试这样的周期 最近几年来 开发环境开始更好地利用编译循环和静态类型提供的信息 IDE 现在编辑抽象语法树(AST) 而不是(或者同时)编辑代码的文本表示 这一策略允许使用强大的代码重构工具 而使用静态类型语言的同样方法来实现此功能则困难得多
静态类型确实能更好地使用工具 但是也存在缺点 强制使用静态类型通常需要编译器 而编译步骤必然会降低生产力 使用 Rails 我可以更改一行代码并重新加载浏览器 就可立即看到更改的结果 与 Java 开发人员相比 大多数 Ruby 开发人员只使用一种很好的编辑器 TextMate 是最流行的 Ruby on Rails 编辑器 它提供了语法突出显示 代码完整性检查 以及一些频繁使用的结构的良好的模板支持 而当发现可将所有简单的基于 Ruby 的脚本(用作基本的 Rails 工具包)放入编辑器中时 您会更加喜出望外 与纯粹的调试器不同的是 我可以使用断点脚本 该脚本可停止特定的应用程序 进入一个 Ruby 解释程序 我可在其中调用方法 检查变量的值 以及甚至在恢复执行之间修改代码
简单的架构
传统的 Web 端 Java 架构包括 一个用于域对象和数据访问对象的层 一个提供业务级 API 的外观层 一个控制器层和一个视图层 此架构比典型的 模型 视图 控制器 架构(使用 Smalltalk 语言最早创建)稍微复杂一些 相比之下 Ruby on Rails 包括一个使用 ActiveRecord 设计模式的模型层 一个控制器层和一个视图层 我们喜欢易于获得的 Rails 方法 它更加简练并且带来额外的复杂性和错误的机会更小
惯例优先原则
Java 框架通常可以自由地使用 XML 配置 而 Rails 主要使用惯例来避免可能的配置 在程序员必须指定配置的位置 Rails 通常依赖 Ruby(常常以 DSL 形式)来提供配置 对于 green field 开发 我发现惯例优先于配置是很有意义的 该策略为我省去了很多行代码 更简化了必须编写的代码 估计我们所需的配置只有传统 Java 应用程序中所指定的十分之一 我们有时会损失一点灵活性 但这并不足以抵消使用此策略带来的节省
总而言之 Rails 框架的原理适合解决 Ch 项目中的问题 集成的各种工具让我可以利用框架实现更多的功能而无需自己进行过多的集成 惯例优先原则 为我节省了配置站点的时间 动态语言为经验丰富的开发人员提供了更多的能力和灵活性 同时也使他们能够利用更少的代码表达更强大的思想 该框架适合于我们团队的能力和要解决的业务问题
持久性
Java 和 Ruby 语言的最流行的持久性框架可以比任何其他特性更好地阐明 Java 和 Ruby 经验之间的区别 Java 开发人员通常使用 Hibernate 它是一种对象关系映射框架 通过 Hibernate 您可获取现有的模型和模式并使用注释或 XML 表达二者之间的映射 Hibernate 类是简单传统 Java 对象(POJO) 它的每个对象派生自一个通用的基类 大多数配置是显式的 使用注释 XML 或二者的某种结合
而 ActiveRecord 是一种包装的框架 就是说每个类都是现有类的包装器 ActiveRecord 根据关联表的内容(如表中每列的一个属性)自动地向模型对象添加特性 所有的类都从一个通用的基类继承 ActiveRecord 主要利用通用约定来推断配置 例如
ActiveRecord 利用类名的复数形式来推出表名 主键的名称为 id 列表的排序顺序由 position 字段决定对象关系映射是使用遗留模式(可能定义时没有考虑对象模型)时的最佳解决方案 但是当您能为应用程序显式地设计数据库模式时 您通常不需要映射框架了 我们将 ActiveRecord 看作我们的一个巨大优点 我们可以包含关系数据库 需要时转入 SQL 并在适当的时候退出
迁移
Rails 迁移使我们能够用代码表示模式的两个版本之间的差别 和它们所包含的数据之间的差别 对每个迁移都进行了命名和编号 可在任何时候恢复到任何版本 迁移有以下一些确切的优点
产生错误代码时可恢复到一个旧版本的模式 用代码而不是 SQL 来表达模式 更便于我们使用 在最大程度上与数据库独立但是迁移也有一些限制 如果两个开发人员同时创建迁移 则编号会出现混乱 所以我们必须手动处理 我们通过有效的通信来使这些问题最小化 团队成员构建需使用迁移的新模型时发出通知 但是这个模型依赖于团队的开发人员较少或迁移进展较慢的情况
ActiveRecord 还有其他的限制 其中一些是故意作出的 Rails 的创建者认为 数据库的约束和组成应归入应用程序而不是数据库 这种思想带来了一些副作用 ActiveRecord 使用视图的情况不是很好 构建过程(克隆模式 复制测试数据并运行测试)并不能正确地进行复制 ActiveRecord 在使用参考完整性约束的某些场合也会出现问题 因为某些类型的关联可能连接到多个数据库表 跨越复杂模型进行预先加载很复杂 通常在连接多行时需要使用 SQL 继承也受到限制 使用 ActiveRecord 时 我被迫使用 单表继承 映射策略 而该策略并不总是最佳选择
所有的持久性策略都充满了妥协 我认为 ActiveRecord 实现了一组有效的妥协 常常选择了简单性 总而言之 ActiveRecord 和迁移是我们的积极推动 我们可以快速地构建解决方案 我们拥有足够的 SQL 访问权可在需要时改进系统性能 但是当 ActiveRecord 并不总能应对挑战时 最好将 Rails 应用于使用老旧模式的项目 一些替代的持久性模型正在出现 包括 RBatis 一种 iBATIS Java 框架的端口 现在讨论 RBatis 的有效性还为时过早
结束语
对于我的团队和项目来说 Ruby on Rails 被证明相当有效 我还不知道这个项目的最终规模如何 因为撰写本文时该系统才运行 个月 现在只是开始增加通信量 但是我们对生产力却很了解 我知道团队的预算比竞争公司(这些公司常常使用 Java 解决方案)的要低得多 我对我们的生产力也很有信心
通过 跨越边界 系列 我向您介绍了 Java 领域以外的语言和解决方案 但程序员毕竟是技术人员 每个高明的技术人员的工具包中都应包含适用于每个解决方案的广泛的工具集 除工具外 本系列中介绍的观点也为您展示了一些其他思路 现在一些框架设计者甚至将 Seaside Rails 中的技术甚至 JavaScript 应用于 Java 框架中 找机会进行同样的应用 继续 跨越边界
关于作者
lishixinzhi/Article/program/Java/hx/201311/26713在 Java中,JavaVM拥有自动管理内存的功能,Java的GC能够进行垃圾回收,但是Android中如果ImageView使用过多的Bitmap的话,经常会报OOM(内存溢出)。
造成内存溢出及解决方案:
1使用BitmapFactorydecodeStream替代createBitmap方法
原因是该方法直读取字节,调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap。
2使用压缩读取技术
BitmapFactoryOptions options = new BitmapFactoryOptions();
optionsinJustDecodeBounds = true;
BitmapFactorydecodeFile(imageSdUri, options);
final int height = optionsoutHeight;
final int width = optionsoutWidth;
optionsinSampleSize = 1;
int w = 320;
int h = 480;
h = wheight/width;//计算出宽高等比率
int a = optionsoutWidth/ w;
int b = optionsoutHeight / h;
optionsinSampleSize = Mathmax(a, b);
optionsinJustDecodeBounds = false;
Bitmap bitmap = BitmapFactorydecodeFile(imageSdUri, options);
3及时释放Bitamp
Bitmap对象在不使用时,我们应该先调用recycle()释放内存,然后才它设置为null虽然recycle()从源码上看,调用它应该能立即释放Bitmap的主要内存,但是测试结果显示它并没能立即释放内存。但是我它应该还是能大大的加速Bitmap的主要内存的释放。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)