Java基础复习笔记03我们不会注意的陷阱
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Java基础复习笔记03我们不太注意的陷阱
刘岩
Email:suhuanzheng7784877@
1.虚拟机对字符串的处理
虚拟机是将字符串直接量(不用new声明的)对象放到一个对象池中去缓存的,第一次使用的时候会将其放入池中,如果下次有其他变量访问一摸一样的直接量的话直接从对象池中去取该直接量,而不是又再生成一个对象。
一般该池中的对象都不会被垃圾回收器回收。
str1和str2指向了该对象池的对象位置。
2.编译时能确定的值
实际上str3和str4在内存池中都是指向了"suhuanzhen123"这个字符串对象,所以输出时true。
因为str4在编译期间就能确定值是suhuanzhen123,所以JVM不会再创建新的对象。
直接从对象池中去取。
3.在编译期间不能确定的值
如果字符串的拼接包含了函数、变量等等编译时不确定因素,那么此时就不能指向对象
4.在编译时就能确定值的字符串变量创建值
实际上仅仅创建了一个对象——suhuanzhen123,缓存到对象池。
5.StringBuilder和StringBuffer的区别
一般在单线程的系统环境下优先使用StringBuilder,因为它是非线程安全的。
而在多线程的环境下,比如Web系统,优先使用StringBuffer,因为它当中绝大部分方法都使用了synchronized进行修饰,保证了多线程环境下的线程安全问题。
6.字符串之间的比较
用一个线程池的常量对象和一个变量、函数返回值比较直接使用==其实就可以了比如:
if ("getparams" == jqgridparams) {
//……………………
}
}
static String getStr(){
return"suhuanzhen123";
}
public static void main(String[] args) {
String str7 = getStr();
System.out.println("suhuanzhen123" == str7);
}
如果是2个字符串变量之间比较就不能用==了
String str7 = getStr();
String str8 = new String("suhuanzhen123");
System.out.println(str8 == str7);
输出false。
System.out.println(str8.equals(str7));
7.
表达式中使用了double类型的数值,那么此表达式会自动向最高级别的基本类型转型,
public static void main(String[] args) {
int num = 1;
double numDoub = num+33.567;
}
就不会报错了。
赋值运算中基本数据转型的级别从高到低排序是String->double->float->long->int->chart、short->byte。
如果运算中含有字符串变量,那么二话不说,此表达式所有的运算最后的返回值就是一
int num = 1;
double numDoub = num+33.567;
String str = num+numDoub+"";
所以最高级别还是String。
8.符合运算符中的隐式类型转换
等价于
也就是说符合运算符自身还隐式地包含了一个强制转型的动作。
这样做有时候会失去精
-3826
9.
第一个循环输出无任何问题,第二个循环报出转型异常。
换几话说,集合本身可以存储不同类型元素的对象的,只不过自从JDK1.5之后出现了泛型,才使我们很多人觉得集合变量仅仅能存一种类型的对象元素。
泛型是为了在编译时更好的排除类型转换带来的繁琐检查。
而在大多数应用中确实集合中就是同类型的元素。
而泛型配合上加强的新循环,它会强制进行单一类型的转型操作。
所以第二个循环的的结果就是
泛型还有可能引起擦除,如下:
在mynew.getId()这一句中实际上编译器这时候仅仅将它识别为Number了,直接当做Integer使用会报错,除非强制转型方可当做Integer来使用。
这里就相当于将泛型信息擦除了,程序只有到了真正运行时环境下才能把其当做Integer来用,编译期间他不认识其实质面目。
Java擦除泛型信息是将该对象所有的泛型信息全部擦除掉,不仅仅是那
执行MyTT my2 = myTT;实际上是擦除了泛型的所有信息,就连之前定义的public List<String> findRows()也被无辜的擦除了泛型信息。
编译时只能认识findRows方法返回的是List而不是List<String>。
直接用String[] array = str.split(".");不起作用,因为”.”代表可以匹配任意字符,必须转义。
11.什么样的对象存在线程安全问题
当我们刚学Java的时候不会考虑多线程的问题,在自己的IDE环境下运行成功了就行了,不会考虑并发使用此程序的时候会出现什么情况。
等做程序员一段时间后发现自己编写的程序确实存在,多线程安全问题。
之后走火入魔似地给自己写的方法加上synchronized。
其实我们有时候没搞懂什么情况下会出现线程安全的问题。
如果我们开发的是单机版的C/S应用系统,客户的计算机就是软件产品的服务器,那么会不会有并发现象呢?只要不是恶意恶搞你的软件,一般一个客户就对应着一个服务,这个时候不存在并发访问的问题,所以一般情况下不会考虑多线程的问题。
如果在基于B/S的Web 系统中,可能服务器是一台机器,世界各地的客户都来访问你开发的系统,为这些世界各地的客户提供服务,这个时候可能会出现线程安全的问题。
为什么说可能,而不是一定呢?如果你的所有的类和类之间的调用都是通过new一个新的对象为之服务(极端情况下还真有),那么new实际上是在服务器内存中新开辟一块内存空间承载对象,那么这种极端情况是不存在线程安全问题的,只要您的服务器内存极大——1TB内存,每日客户量在2万人左右,估计运行1个月应该差不多没什么问题。
不过考虑成本,一般没这样高级的服务器让这种极端的程序运行吧。
那么最多的就是类之间的调用不是永远的new一个新的出来,而是为了节省资源,使用对象缓存池或者干脆整个应用软件调用比
较频繁的类就使用一个单例对象就得了
,每次使用这个对象都是原来已有的,不必在内存新建一个,这样垃圾回收器的工作量也不会那么大。
而恰恰是这个时候,这个复用的对象就会出现线程安全的问题。
所以在开发Web系统的时候大家一定要小心自己编写的类给别人调用的时候是否存在线程安全的问题。
Thread-0从numIhread=1下次访问的时候一下子变成了numIhread=8,谁干的?这都是Thread-1影响的。
改进程序如下
注意红色字体和蓝色字体,2个线程互不侵犯,拿到的numThread是第一个线程的变量副本,因此互不干扰,你走你的阳关道,我走我的独木桥。
12.用synchronized和ThreadLocal的区别
其实网上已经很多资料介绍这2个的区别了,synchronized表示阻塞,是一种用时间换取空间的做法,而ThreadLocal呢是用资源空间换取时间的做法。
synchronized表示一旦某个线程过来了,使用了此修饰后的方法或者代码块,那么其他任何线程不允许访问,直到此方法或者代码块都走完了,其他那些排队的线程再来执行,也就是说再某一个时刻,这个方法的权柄完全由单独一个线程把持着,别人别想越雷池一步,谁让你不早点来的,等老子使用完了,你再用吧。
这就好比合租房子的厕所,厕所是个资源,一旦被人使用了,不好意思,其他人排队等着吧。
而ThreadLocal呢,相当于大家上的公用厕所,而且只要是地球空间允许(对应于内存空间),那么将是无限个坑位,嘻嘻,爽吧,想上就上,而且互不打扰,你拉你的,我拉我的,而且大家的坑位都是从同一个模板构造出来的,不会出现不同的人使用不同样式的坑位,比如残疾人、老年人。
一视同仁!
ThreadLocal底层代码使用一个静态的内部类ThreadLocalMap存储副本变量,好让另一个线程使用(都是爷,它谁也不敢怠慢)。
13.线程的启动
至于线程的调用,还有一点就是只要是线程,无论是继承自Thread类的还是实现类Runnable接口的,都必须使用start()方法作为启动线程的标识。
如果不调用start()方法,那么他不能算是以线程为单元执行。
14.静态同步方法
在静态方法前用synchronized修饰实际上是将整个静态类对象的该方法上了锁,也就是说锁定对象不是实例对象,而是类对象,所有的实例对象都是有内存中的仅有的一个类对象得来的。
如果这个静态方法没有访问(或者说改变更贴切一些)任何的静态成员变量,那么其实进行加锁限定除了降低时间效率外没太大的作用。
是很大,加入这个静态方法法十分消耗时间,那么另一个线程会等待很长时间才能执行此方法。
那么这个时候加上synchronized还真不行,这就是典型的占着皇帝的宝座,不为老百姓办事!还不让别人坐,结果很显然,只能造反(系统崩溃),才能解决问题。
15.switch语句的用法
有人说:“笔者基础也太差了,switch都得说说?”是啊?哥们得说说啊。
试问,自从你学了分支语句后,你使用if else多啊?还是用switch的情况多啊?很肯定,很多人在开发中switch几乎没怎么用,使用if else不容易出错,之后就渐渐淡忘了switch的用法。
现在咱们来复习一下。
量的一种可能性,如果没有任何case符合,那么就进入default处理。
每一个分支都有一个break,代表如果符合此分支的情况,处理完毕后会退出switch块。
如果没有break,那么会继续往下面的case走,无论下面的case是否符合都会执行,直到遇到break或者整个switch块走完方才停止。
表达式的类型只有Java个别的基本类型和枚举——byte、short、int、char、enum。
其他类型都不能作为预判断变量。
16.方法的重载
JVM在识别方法的时候有一定的智能型,传入的实参如果和定义的形参不一样的话,那
匹配double,如果重载方法还有更精确的形参,那么自然会去匹配最精确的方法了。
17.非静态内部类
非静态内部类实际上需要外部类才能顺利创建实例,非静态内部类的构造函数,JVM都做了特别的处理,将外部类对象作为默认构造器的第一个隐式参数。
而且非静态内部类有个限制,不能拥有静态成员变量,因为非静态的内部类本身就处在一个非静态的上下文环境中
18.静态内部类
如果非使用静态类不可,其实大多数都是用静态内部类,因为静态内部类可以访问外部类的静态成员变量。
而换句话说,静态内部类不能访问外部类的非静态成员变量。
所谓静态static,就是理解成类对象级别的,非静态成员变量是实例对象级别的,属于在类对象创建完了它才创建呢。
怎么能在类对象中使用一个还没创建出来的东东呢?
19.异常处理
finally的使用:一般在finally中回收一些资源,比如数据库连接的关闭、IO文件流的通道关闭、临时变量的指针回收等等操作。
唯一存有疑问的可能就是finally的执行时机。
到底是在return之前还是之后,实际上如果一个try块存在return了,那么如果这段try 块有相应的finally的话那么return操作先挂起,之后执行finally中的代码,如果finally 代码块没有return,或者其他影响try块的语句的话,那么执行完finally代码段之后再执行return语句。
什么情况下finally中会影响try中的return执行呢?就是finally中也存在着return语句,或者System.exit(0);这种系统级关闭的语句。
那么try的return是不会执行的。
还有一点就是即使try块抛出异常了,finally会执行吗?
finally块已经正常返回了。
catch到一个异常后不应该在catch中执行任何与应用有关的业务逻辑了,而是应当及时反映异常,比如打印到控制台、发送错误消息、记录error日志、发送错误的消息(web service、JMS、socket)等等操作。
如果在catch中进行业务处理有可能是死循环,将资源都吃掉不说,真正导致异常的错误信息估计此时也已经错过了。
的异常类。
遇到某某自定义的异常后尝试继续执行某业务,这个场景虽然极端,但是确实存在,只不过这样的处理有点违背Java设计异常机制的初衷。
(注:可编辑下载,若有不当之处,请指正,谢谢!)。