为什么javax不能解析 java一般怎么学习呢
大家好,今天来为大家解答为什么javax不能解析这个问题的一些问题点,包括java一般怎么学习呢也一样很多人还不知道,因此呢,今天就来为大家分析分析,现在让我们一起来看看吧!如果解决了您的问题,还望您关注下本站哦,谢谢~
给讲讲java接口的概念!
希望你仔细阅读。对初学者来说,接口不是很好理解。我教过的学生也都提出过这个问题。
我们来看一个类
class A{
private int a;
public int getA(){
return a;
}
}
这个类的属性是私有的,外界不能访问,而外界可以通过公有方法来访问这个类。我们说一个类的公有方法就是这个类的对外接口。通常
一个类的属性都是私有的,方法大多是公有的。外界只能过个这些公有方法来访问类。这也是Java封装性的体现。如果一个类没有公有属性,
也没有公有方法,这个类就是无法使用的类了。所以我们需要为一个类提供对外接口。
一个类中的方法,不只是说明了它要“做什么”,而且方法的内容也说明了“怎么做”。打个不太恰当的例子,一个杀人方法。从名字上
看,你知道了“做什么”,但没有办法看到“怎么做”。而方法的内容说明了“怎么做”。
class killer{
private String name;
private int age;
private String phone;
private String addr;
......
public void kill(Person p){
Qiang qiang= new Qiang("ak47");
qiang.fire(p);
}
}
这个类的kill方法内容说明了杀人的过程。如果你不想用这种方式杀人。因为你很BT,你想用毒药杀人。那么这个类的内容就需要改。但
是,还有很多其它的“客户”,需要用不同的方式杀人。怎么办呢?一个很好的办法就是,我们只定义“做什么”,而不定义“怎么做”。
interface Killer{
public void kill(Person p);
}
接口说明了“做什么”,而实现这个接口的类,也就是实现类需要说明“怎么做”。
class Killer1 implements Killer{
public void kill(Person p){
Qiang qiang= new Qiang("ak47");
qiang.fire(p);
}
}
class Killer2 implements Killer{
public void kill(Person p){
Bane bane= new Bane();
p.eat(bane);
}
}
public class Test{
public static void main(String[] args){
Killer jingKe= new Killer1();
Person yingZheng= new Person();
jingKe.kill(yingZheng);
}
}
接口可以把“做什么”和“怎么做”分离开来。这给Java带来了很多好处。虽然代码量增加了,可我们的程序的可维护性加强了。我们的程序是可以拆分的。就象电脑一样,可以拆分成很多组件。我一直在想,如果我的MP3耳机可以拆分就好了,那样在耳机只坏掉一个的时候就不用重新买一个了。
不过上面的例子看不到什么太大的好处。你可能会说,如果你
的目的是为了不修改代码,那么,如果我想使用Killer2来完成任务,还是需要修改main方法为:Killer jingKe= new Killer2();。没有错,
不过你可以通过一个工厂来完成上面的任务。也就是说,不通过new语句来获得Killer对象,而是通过工厂来获得Killer对象。
public class KillerFactory{
public static killer getKiller(){
return new Killer1();
}
public static Killer getKiller(String path) throws Exception{
Properties prop= new Properties();
prop.load(new FileInputStream(path));
String className= prop.getProperty("killer");
Class clazz= Class.forName(className);
return(Killer)clazz.newInstance();
}
}
代码确实增加了很多,可是这对后期的系统维修和系统升级带来了很多好处。
水龙头与出水管的关系,我们需要把水龙头安装到出水管上。如果有一天我们需要更换水龙头时,只需要把老的水龙头拆卸下来,把新的
水龙头安装到出水管上既可。如果水龙头与出水管是一体的,就是无法拆卸的怎么办呢?或是说出水管只能安装水龙头,而不能安装淋浴器,
这就使我们生活很不方便。我们可以理解为出水管的连接方法,连接的对象是“出水设备”,而这个“出水设备”是一个接口。而水龙头与淋
浴器都是这个接口的实现类。但是接口在哪里呢?它长什么样子?我们没看到。它是一个标准,连接处的内径与外径。螺丝抠的密度等。这就
和你的电脑上为什么可以连接USB设备一样。如果电脑和某一个USB设备电焊到一起,那么其它的USB设备就无法使用了。电脑使用的是实现了
USB接口的电子设备,而我们的U盘、MP3、移动硬盘及鼠标都是USB接口的实现类。
用Java写出来的程序也和我们现实生活中的设备一样。如电脑,我们希望电脑的所有部件都可以更换,如果主板上的内存插槽与内存条不
附。我们说,内存条没有实现某某接口。Java是完全面向对象的,而面向对象是我们最为熟悉的东东。面向对象并不简单,而是我们太熟悉它
了。所以我们学习Java会很方便。在现实社会中,所有的标准到了Java中都是接口。一盒香烟多少支烟,烟的长度等都是标准。一个光盘的大
小。Java中的JDBC就是一个标准,而各大数据库厂商及第三方厂商实现了这一标准。JDBC只说明了目的,没有说明怎么完成的目的。
面向对象就在我们眼前,不过我们有时不太注意它。希望你在今后学习Java时,多与现实社会联系。这样可以有利与你的理解。
代码量加大了,但对后期的维护与升级提供了方便。软件公司卖给客户的是class文件,而不是java文件。如果你的客户需要更换Killer对
象,只需修改资源文件既可。
下面我们来看一个定时器类。现在什么都是自动化的,如空调、电视、洗衣机等等。都要用到定时器这个类。对了,还有定时炸弹也要用
它。那我们可不可以只写一个定时器类,应用到所有的需要定时器的设备上呢?答案是肯定的,我们需要这个类。
好了,我们来分析一下定时器需要完成什么工作吧。定时器应该有启动、停止方法。定时器启动之后,每过一定时间就执行某个动作。其
中时间间隔为long型,而要执行的动作可能是输出一个字符串,也可能是打印作业。具体要干什么由使用定时器的用户来完成。而定义定时器
时,根本就不知道要干什么。
public class Timmer{
private long time;
private Action action;
public Timmer(){}
public Timmer(long time, Action action){
this.time= time;
this.action= action;
}
public void start(){
state= START;
if(th== null){
th= new Thread(){
public void run(){
while(state== START){
try{
Thread.sleep(time);
action.action();
} catch(Exception e){
}
}
}
};
}
th.start();
}
public void stop(){
state= STOP;
}
public void setTime(long time){
this.time= time;
}
public void setAction(Action action){
this.action= action;
}
public long getTime(){
return(this.time);
}
public Action getAction(){
return(this.action);
}
}
Action是一个接口,它只有一个方法,就是要完成的任务。我们在定时器启动时调用这个接口的方法。而这个Action接口的对象,代表一
个动作,这个动作就是用户要完成的动作。
public interface Action{
public void action();
}
public static void main(String[] args){
Timer t= new Timer(2000, new Action(){
public void action(){
System.out.println("Hello World!");
}
});
t.start();
javax.swing.JOptionPane.showMessageDialog(null,"点击确定按钮停止定时器");
t.stop();
System.exit(0);
}
这是一个典型的回调方法例子。在AWT中,java使用了大量的监听器。这些监听器都是回调方法。在XML解析器SAX中,也使用了回调方法来解析XML文档。
接口要比抽象类还要抽象。抽象类需要子类继承,而Java是单继承,所以抽象类被限制了。而接口不同,一个类可以实现多个接口。好比人类与程序员类之间的关系。可以说程序员是人类的子类,如果程序员是一个接口。用人类的子类来实现它就会更好。这个子类还可以去实现会计接口、音乐家接口等等。
在struts2.0、spring、hibernate等框架中,都大量使用接口。我们关心的是某个接口与另一个接口之间的关系。而不关心某个实现类与另一个接口实现类的关系。在客观世界中,我们交谈时都大量使用接口,只是我们没有注意罢了。如:我公司需要一个程序员(一个实现了程序员接口的对象)。上课时,讲师有一台计算机,用白板笔在白板上写字(计算机是接口,白板及白板笔也是接口)。讲师希望学生能学会所有的知识(讲师及学生都是接口)。
就说这么多了,还有什么不明白可以与我联系。QQ:51038648
我在北京中科院计算所培训中心做兼职java讲师。我的经验是,要想学好Java,一定要多打代码、吃透代码。如果不勤奋,就一定学不好。看来你是刚刚开始学习Java,你后面的路还很长,而且不是很好走。
java一般怎么学习呢
工资高不一定你就能够学会,如果谁都能学会那他工资还有这么高吗
如果实在想学,建议你先去网上找找视频看看Java了再去学
Java学习路线:
JavaSE->数据库->JDBC->前端基础->JQuery->JavaWEB->Spring->MyBatis->Maven->SpringBoot->SpringCloud->Redis->Git->Linux
整体教程目录:
项目源码:
微服务项目:
项目优化:
Linux:
阶段1 java语言基础
1-1-Java基础语法
1、第1节 java运行环境提取码:8ax6
2、第2节 HelloWorld案例提取码:us3j
3、第3节关键字&标识符提取码:13dl
4、第4节常量&变量提取码:6331
5、第5节数据类型转换提取码:9glo
6、第6节运算符提取码:ys2n
7、第7节方法入门提取码:b1ib
8、第8节 JDK9新特性-Jshell提取码:ounw
9、第9节选择结构-if语句-switch语句提取码:0d14
10、第10节循环结构-for-while-do..while提取码:k2ig
11、第11节开发工具-IDEA提取码:404i
12、第12节方法复习提取码:ulku
13、第13节方法重载提取码:wfkr
14、第14节数组提取码:p8ml
1-2-面向对象和封装
1、1_2_1_01_面向对象思想的概述提取码:599q
2、1_2_1_02_面向对象思想的举例提取码:lv2s
3、1_2_1_03_类和对象的关系提取码:7rs3
4、1_2_1_04_类的定义提取码:xvhx
5、1_2_1_05_对象的创建及其使用提取码:xsal
6、1_2_1_06_手机类练习提取码:lnho
7、1_2_1_07_一个对象的内存图提取码:wnaz
8、1_2_1_08_两个对象使用同一个方法的内存提取码:thet
9、1_2_1_09_两个引用指向同一个对象的提取码:3he6
10、1_2_1_10_使用对象类型作为方法的参提取码:73w4
11、1_2_1_11_使用对象类型作为方法的返提取码:gjmn
12、1_2_1_12_成员变量和局部变量的区别提取码:eqep
13、1_2_1_13_面向对象三大特征之封装性提取码:zlcz
14、1_2_1_14_private关键字的作用及使用提取码:4i77
15、1_2_1_15_练习使用private关键字定义提取码:dctu
16、1_2_1_16_this关键字的作用提取码:x68z
17、1_2_1_17_构造方法提取码:8xkz
18、1_2_1_18_定义一个标准的类提取码:opmf
1-3-Java语言高级
1、01-常用API_1提取码:alfe
2、02-继承与多态提取码:h5o7
3、03-常用API第二部分提取码:olnf
4、04-集合提取码:40ca
5、05-异常与多线程提取码:mu40
6、06-File类与IO流提取码:4bpt
7、07-网络编程提取码:s5ks
8、08-JDK8新特性提取码:jnbj
9、09-基础加强提取码:1ngz
10、10-MySQL提取码:30y4
11、11-JDBC提取码:15q7
阶段2 JavaWeb+旅游网
01 HTML和CSS
1、第1节概念介绍提取码:dal6
2、第2节基本标签提取码:4uyq
3、第3节表单标签提取码:h1ok
4、第4节 CSS概述提取码:ubyx
5、第5节 CSS_选择器提取码:zep7
6、第6节 CSS属性提取码:9l4h
7、第7节 CSS_案例-注册页面提取码:onc6
02 JavaScript
1、01 JavaScript_简介提取码:2efk
2、02 JavaScript基础语法提取码:bl89
3、03 JavaScript运算符提取码:v9sh
4、04 JavaScript特殊语法提取码:yj6p
5、05 JavaScript_语法_流程控制语提取码:tx9j
6、06 JavaScript_对象提取码:df4q
7、07 DOM和事件的简单学习提取码:ljt5
8、08 BOM对象提取码:jwwy
9、09 DOM对象提取码:ukah
10、10JavaScirpt中的事件提取码:ab1w
03 BootStrap
1、01快速入门提取码:5jhm
2、02Bootstrap_栅格系统提取码:1xuu
3、03Bootstrap_全局CSS样式提取码:fjka
4、04Bootstrap_组件和插件提取码:g5wp
5、05案例_旅游网提取码:jxy3
04 XML
1、01 xml基础提取码:jose
2、02 xml_约束提取码:q0xr
3、03 xml_解析提取码:bbsn
05 Tomcat
1、01web相关概念提取码:p2az
2、02 tomcat基本操作提取码:zqqx
3、03 tomcat_部署项目提取码:i4a2
06-Servlet和HTTP请求协议
1、01Servlet_快速入门提取码:ah8k
2、02Servlet_生命周期方法提取码:w7t9
3、03Servlet_3.0注解配置提取码:d8zy
4、04 IDEA与tomcat相关配置提取码:5rvl
5、05 Servlet_体系结构与urlpartten配置提取码:qf3i
6、06 HTTP请求协议提取码:5f7v
07-Request和Respons
1、01 Request原理和继承体系提取码:3hxr
2、02 Request_获取请求数据提取码:oxl7
3、03请求转发和request共享数据提取码:9xbq
4、04 Request_获取ServletContext提取码:yfaz
5、05 request登录案例提取码:owgj
6、06 HTTP响应协议提取码:xs8k
7、07 response之重定向提取码:i8de
8、08 response之相对路径和绝对路径提取码:6p8q
9、09 response之输出数据提取码:54fe
10、10 response之验证码提取码:uy06
11、11 ServletContext对象提取码:yopc
12、12文件下载提取码:szv9
08-Cookie和Session
1、第1节 Cookie快速入门提取码:ncss
2、第2节 Cookie_细节提取码:la7v
3、第3节 Cookie案例提取码:mamn
4、第4节 JSP改造Cookie案例提取码:fpkd
5、第5节 Session快速入门提取码:0tyf
6、第6节 Session细节提取码:obu1
7、第7节 Session之验证码案例提取码:fxtt
09-JSP,EL和JSTL
1、第1节 JSP基础语法提取码:7rvw
2、第2节 MVC提取码:ywmz
3、第3节 EL介绍和运算符提取码:5gxf
4、第4节 EL获取域中存储的数据提取码:yha0
5、第5节 JSTL常用标签提取码:mr92
10-综合案例(用户信息)
1、第1节登录功能提取码:d7v4
2、第2节添加删除修改功能提取码:40df
3、第3节删除选中功能提取码:jhlx
4、第4节分页查询功能提取码:8mxb
5、第5节复杂条件查询功能提取码:qj8m
11-Filter和Listener
1、第1节 Filter快速入门提取码:7qrm
2、第2节 Filter细节提取码:s2w9
3、第3节 Filter案例提取码:f53s
4、第4节监听器提取码:kjwy
12-Jquery
1、第1节 JQuery基础提取码:0uj1
2、第2节 JQuery选择器提取码:o85w
3、第3节 Jquery基础案例提取码:5u3t
4、第4节 JQuery动画和遍历提取码:ut21
5、第5节 JQuery事件绑定和切换提取码:e5sv
6、第6节 Jquery高级案例提取码:ytj4
13-Ajax和JSON
1、第1节原生JS方式实现Ajax提取码:xh70
2、第2节 JQuery方式实现Ajax提取码:woaa
3、第3节 JSON基础语法提取码:pqhj
4、第4节 JSON_解析器提取码:zbzx
14-Redis
1、第1节 Redis环境搭建提取码:poob
2、第2节 Redis命令操作提取码:bq6d
3、第3节 Redis持久化提取码:0x4x
4、第4节 Jedis代码操作提取码:bon3
5、第5节 Jedis连接池提取码:hu0n
6、第6节 redis案例提取码:cosn
15-Maven基础
1、第1节基本概念提取码:4fok
2、第2节 maven的安装和仓库种类提取码:gw1r
3、第3节 maven标准目录结构和常用命令提取码:3jn7
4、第4节 maven生命周期和概念模型图提取码:oqrb
5、第5节使用骨架创建maven的java工程提取码:k26p
6、第6节 maven工程servlet实例提取码:xdmw
16-旅游网
1、01准备工作提取码:uprl
2、02注册功能提取码:9pf5
3、03登陆和退出功能提取码:0hkk
4、04 BaseServlet抽取提取码:qswt
5、05分类数据展示功能提取码:649n
6、06旅游线路分页展示分页展示提取码:o6v6
7、07旅游线路查询提取码:pzjb
8、08旅游线路详情提取码:wtke
9、09旅游线路收藏提取码:3236
阶段3 1.Mybatis
1、01.Mybatis课程介绍及环境搭建提取码:6zwc
2、02.Mybatis入门案例提取码:2aoi
3、03.自定义Mybatis框架提取码:r9wc
4、04.自定义Mybatis框架基于注解开发提取码:1jzg
5、05.使用Mybatis完成CRUD提取码:d4b7
6、06.使用Mybatis完成DAO层的开发提取码:dzvr
7、07.Mybatis的连接池及事务提取码:ij3e
8、08.动态SQL提取码:yiys
9、09.Mybatis的多表操作提取码:dt9w
10、10.JNDI扩展知识提取码:u5fx
11、11.Mybatis的缓存提取码:ng54
12、12.Mybatis注解开发提取码:yh6z
阶段3 2.Spring
1、01.Spring框架简介提取码:va1x
2、02.程序间耦合提取码:s8cg
3、03.Spring的 IOC和 DI提取码:59of
4、04.Spring的常用注解提取码:tfw5
5、05.基于XML的IOC的案例1提取码:1gf9
6、06.Spring的新注解提取码:pjzs
7、07.银行转账案例提取码:um2b
8、08.面向切面编程 AOP提取码:4tv2
9、09.JdbcTemplate的基本使用提取码:vjxx
10、10.Spring中事务控制提取码:rx3v
阶段3 3.SpringMVC
1、01.SpringMVC概述及入门案例提取码:e2ty
2、02.参数绑定及自定义类型转换提取码:657g
3、03.SpringMVC常用注解提取码:emrc
4、04.SpringMVC返回值类型及响应数据类型提取码:bd9t
5、05.文件上传提取码:pzy7
6、06.异常处理及拦截器提取码:7a2y
7、07.SSM整合案例提取码:lzzd
阶段4 3.Spring Data JPA
1、01.JAP的引入提取码:o61r
2、02.JPA的入门案例提取码:ld9u
3、03.主键生成策略提取码:f8ri
4、04.JPA的基本操作提取码:yxcv
5、05.JPQL查询提取码:cxwq
6、06.spring data jpa简介提取码:678v
7、07.入门案例提取码:2u3l
8、08.执行过程分析提取码:upe4
9、09.spring data jpa查询提取码:smsc
10、10.动态查询提取码:qoxx
11、11.多表操作-一对多提取码:fai2
12、12.多表操作-多对多提取码:bvvg
13、13.对象导航查询提取码:ietq
阶段4 4.Spring Boot
1、01.spring boot介绍提取码:fzi8
2、02.spring boot入门提取码:bp25
3、03.spring boot原理分析提取码:8yer
4、04.spring boot配置文件提取码:wvoc
5、05.spring boot集成提取码:ywin
阶段4 5.Git
1、01.Git简介及安装使用提取码:a0kx
2、02.连接远程仓库提取码:kziq
3、03.Git分支提取码:5rm2
原文链接:https://zhuanlan.zhihu.com/p/2
java中的空指针异常怎么解决
原文:https://www.zhihu.com/question
你这个问题的解决
问题定位:
在堆栈异常信息的第一行就可以定位到是哪里出了空指针,倘若这里不是你写的类,可以往下翻一下,找到你写的类,就是这里出现的空指针。
问题解决:
对一个空对象调用里面的方法或者属性的时候会报空指针,检查这个对象为什么是空即可。
Java空指针异常的若干解决方案
Java中任何对象都有可能为空,当我们调用空对象的方法时就会抛出 NullPointerException空指针异常,这是一种非常常见的错误类型。我们可以使用若干种方法来避免产生这类异常,使得我们的代码更为健壮。本文将列举这些解决方案,包括传统的空值检测、编程规范、以及使用现代 Java语言引入的各类工具来作为辅助。
运行时检测
最显而易见的方法就是使用 if(obj== null)来对所有需要用到的对象来进行检测,包括函数参数、返回值、以及类实例的成员变量。当你检测到 null值时,可以选择抛出更具针对性的异常类型,如 IllegalArgumentException,并添加消息内容。我们可以使用一些库函数来简化代码,如 Java 7开始提供的 Objects#requireNonNull方法:
public void testObjects(Object arg){
Object checked= Objects.requireNonNull(arg,"arg must not be null");
checked.toString();}
Guava的 Preconditions类中也提供了一系列用于检测参数合法性的工具函数,其中就包含空值检测:
public void testGuava(Object arg){
Object checked= Preconditions.checkNotNull(arg,"%s must not be null","arg");
checked.toString();
}
我们还可以使用 Lombok来生成空值检测代码,并抛出带有提示信息的空指针异常:
public void testLombok(@NonNull Object arg){
arg.toString();
生成的代码如下:
public void testLombokGenerated(Object arg){
if(arg== null){
throw new NullPointerException("arg is marked@NonNull but is null");
}
arg.toString();
}
这个注解还可以用在类实例的成员变量上,所有的赋值操作会自动进行空值检测。
编程规范
·通过遵守某些编程规范,也可以从一定程度上减少空指针异常的发生。
使用那些已经对 null值做过判断的方法,如 String#equals、String#valueOf、以及三方库中用来判断字符串和集合是否为空的函数:
if(str!= null&& str.equals("text")){}
if("text".equals(str)){}
if(obj!= null){ obj.toString();}
String.valueOf(obj);//"null"
// from spring-core
StringUtils.isEmpty(str);
CollectionUtils.isEmpty(col);
// from guava
Strings.isNullOrEmpty(str);
// from commons-collections4
CollectionUtils.isEmpty(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<>()
jdbcTemplate.queryForList("SELECT* FROM person");
//若找不到该条记录,则抛出 EmptyResultDataAccessException
jdbcTemplate.queryForObject("SELECT age FROM person WHERE id= 1", Integer.class);
//支持泛型集合
public<T> List<T> testReturnCollection(){
return Collections.emptyList();
}
静态代码分析
Java语言有许多静态代码分析工具,如 Eclipse IDE、SpotBugs、Checker Framework等,它们可以帮助程序员检测出编译期的错误。结合@Nullable和@Nonnull等注解,我们就可以在程序运行之前发现可能抛出空指针异常的代码。
但是,空值检测注解还没有得到标准化。虽然 2006年 9月社区提出了 JSR 305规范,但它长期处于搁置状态。很多第三方库提供了类似的注解,且得到了不同工具的支持,其中使用较多的有:
javax.annotation.Nonnull:由 JSR 305提出,其参考实现为 com.google.code.findbugs.jsr305;
org.eclipse.jdt.annotation.NonNull:Eclipse IDE原生支持的空值检测注解;
edu.umd.cs.findbugs.annotations.NonNull:SpotBugs使用的注解,基于 findbugs.jsr305;
org.springframework.lang.NonNull:Spring Framework 5.0开始提供;
org.checkerframework.checker.nullness.qual.NonNull:Checker Framework使用;
android.support.annotation.NonNull:集成在安卓开发工具中;
我建议使用一种跨 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>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
<version>3.1.7</version>
</dependency>
以下是对不同使用场景的说明:
@NonNullprivate Object returnNonNull(){
//错误:returnNonNull()可能返回空值,但其已声明为@Nonnull
return null;
}
@CheckForNull
private Object returnNullable(){
return null;
}
public void testReturnNullable(){
Object obj= returnNullable();
//错误:方法的返回值可能为空
System.out.println(obj.toString());
}
private void argumentNonNull(@NonNull Object arg){
System.out.println(arg.toString());
}
public void testArgumentNonNull(){
//错误:不能将 null传递给非空参数
argumentNonNull(null);
}
public void testNullableArgument(@CheckForNull Object arg){
//错误:参数可能为空
System.out.println(arg.toString());
}
对于 Eclipse用户,还可以使用 IDE内置的空值检测工具,只需将默认的注解 org.eclipse.jdt.annotation.Nullable替换为 SpotBugs的注解即可:
Checker Framework与@NonNull、@Nullable
Checker Framework能够作为 javac编译器的插件运行,对代码中的数据类型进行检测,预防各类问题。我们可以参照官方文档,将 Checker Framework与 maven-compiler-plugin结合,之后每次执行 mvn compile时就会进行检查。Checker Framework的空值检测程序支持几乎所有的注解,包括 JSR 305、Eclipse、甚至 lombok.NonNull。
import org.checkerframework.checker.nullness.qual.Nullable;@Nullable
private Object returnNullable(){
return null;
}
public void testReturnNullable(){
Object obj= returnNullable();
//错误:obj可能为空
System.out.println(obj.toString());
}
Checker Framework默认会将@NonNull应用到所有的函数参数和返回值上,因此,即使不添加这个注解,以下程序也是无法编译通过的:
private Object returnNonNull(){//错误:方法声明为@NonNull,但返回的是 null。
return null;
}
private void argumentNonNull(Object arg){
System.out.println(arg.toString());
}
public void testArgumentNonNull(){
//错误:参数声明为@NonNull,但传入的是 null。
argumentNonNull(null);
}
Checker Framework对使用 Spring Framework 5.0以上的用户非常有用,因为 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。
StringUtils.capitalize(null);
String filename= StringUtils.getFilename("/path/to/file");
//错误:filename可能为空。
System.out.println(filename.length());
Optional类型
Java 8引入了 Optional<T>类型,我们可以用它来对函数的返回值进行包装。这种方式的优点是可以明确定义该方法是有可能返回空值的,因此调用方必须做好相应处理,这样也就不会引发空指针异常。但是,也不可避免地需要编写更多代码,而且会产生很多垃圾对象,增加 GC的压力,因此在使用时需要酌情考虑。
Optional<String> opt;//创建
opt= Optional.empty();
opt= Optional.of("text");
opt= Optional.ofNullable(null);
//判断并读取
if(opt.isPresent()){
opt.get();
}
//默认值
opt.orElse("default");
opt.orElseGet(()->"default");
opt.orElseThrow(()-> new NullPointerException());
//相关操作
opt.ifPresent(value->{
System.out.println(value);
});
opt.filter(value-> value.length()> 5);
opt.map(value-> value.trim());
opt.flatMap(value->{
String trimmed= value.trim();
return trimmed.isEmpty()? Optional.empty(): Optional.of(trimmed);
});
方法的链式调用很容易引发空指针异常,但如果返回值都用 Optional包装起来,就可以用 flatMap方法来实现安全的链式调用了:
String zipCode= getUser().flatMap(User::getAddress)
.flatMap(Address::getZipCode)
.orElse("");
Java 8 Stream API同样使用了 Optional作为返回类型:
stringList.stream().findFirst().orElse("default");stringList.stream()
.max(Comparator.naturalOrder())
.ifPresent(System.out::println);
此外,Java 8还针对基础类型提供了单独的 Optional类,如 OptionalInt、OptionalDouble等,在性能要求比较高的场景下很适用。
其它 JVM语言中的空指针异常
Scala语言中的 Option类可以对标 Java 8的 Optional。它有两个子类型,Some表示有值,None表示空。
val opt: Option[String]= Some("text")opt.getOrElse("default")
除了使用 Option#isEmpty判断,还可以使用 Scala的模式匹配:
opt match{case Some(text)=> println(text)
case None=> println("default")
Scala的集合处理函数库非常强大,Option则可直接作为集合进行操作,如 filer、map、以及列表解析(for-comprehension):
opt.map(_.trim).filter(_.length> 0).map(_.toUpperCase).getOrElse("DEFAULT")val upper= for{
text<- opt
trimmed<- Some(text.trim())
upper<- Some(trimmed) if trimmed.length> 0
} yield upper
upper.getOrElse("DEFAULT")
Kotlin使用了另一种方式,用户在定义变量时就需要明确区分可空和不可空类型。当可空类型被使用时,就必须进行空值检测。
var a: String="text"a= null//错误:无法将 null赋值给非空 String类型。
val b: String?="text"
//错误:操作可空类型时必须使用安全操作符(?.)或强制忽略(!!.)。
println(b.length)
val l: Int?= b?.length//安全操作
b!!.length//强制忽略,可能引发空值异常
Kotlin的特性之一是与 Java的可互操作性,但 Kotlin编译器无法知晓 Java类型是否为空,这就需要在 Java代码中使用注解了,而 Kotlin支持的注解也非常广泛。Spring Framework 5.0起原生支持 Kotlin,其空值检测也是通过注解进行的,使得 Kotlin可以安全地调用 Spring Framework的所有 API。
结论
在以上这些方案中,我比较推荐使用注解来预防空指针异常,因为这种方式十分有效,对代码的侵入性也较小。所有的公共 API都应该使用@Nullable和@NonNull进行注解,这样就能强制调用方对空指针异常进行预防,让我们的程序更为健壮。
为什么javax不能解析和java一般怎么学习呢的问题分享结束啦,以上的文章解决了您的问题吗?欢迎您下次再来哦!