JAVA程序设计<5>
1.Java程序设计基本概念
1.1 JVM
ClassLoader(类加载器):每一个java文件都对应一个java类,根据程序需要逐渐载入内存。(一般ExtClassLoader加载java的扩展API即/lib/ext中的类,APPClassLoader用来加载用户机器上的CLASSPATH设置的目录中的class)。
ClassLoader加载流程:当程序运行时,JVM启动,运行 bootstrap classloader(启动类加载器),该ClassLoader加载Java核心API(Extclassloader和AppClassLoader也在此时被加载),调用Extclassloader加载扩展API,最后Appclassloader加载CLASSPATH目录下定义的class。----一个程序最基本加载流程
JVM内部,统一使用编码是Unicode表示,编码转换只能发生在边界地方,JVM和OS的交界处,也就各种输入输出(或者Reader,Writer类)起作用的地方。(所以面向字节和面向字符有差别)
1.2 i++
i++是程序完毕后自增,++i是程序开始前自增
i=0;i=i++ + ++i=0+2=2
1.3 类型转换
布尔型 数值型 字符型
低级转高级自动转化(如果是char类型转高级类型,自动转对应ASCII码)
强制类型转化:(类型)变量名
包装类过度类型转化
字符串与其他类型转化(toString()转字符串)
1.4 程序结构
assert(表达式) 断言
1.5 运算符
& &&(和前面一个&区别是只要前面false后面无需判断)
1.6 异常
java程序运行时出现的非正常现象,这种情况称之为运行错误。 Error(由java虚拟机生成并抛出5)和Exception
final(变量值不变或者对象引用不变)、finall、finalize(最多运行一次)
1.7 反射----是指程序可以访问、检测和修改它本身的状态或行为的一种能力。
反射允许在编写与执行时,使程序代码能够接入装载到JVM中的类的内部信息,而不是源代码中选中的的类协作代码。需要注意的是如果使用不当,反射的成本会很高。
java中的类反射Relectiion是java程序语言开发的特点之一,它允许运行中的java程序对自身进行检查,并能直接操作程序的内部属性。
2 Java内存管理
2.1垃圾收集
显示的分配内存和释放内存常常引起“内存泄漏”,因此,java在创建对象时会自动分配内存,并在该对象引用不存在自动释放内存。
垃圾收集器技术监视java程序运行。java使用一系列软指针来跟踪对象的各个引用,并用一个对象表将这些软指针映射为对象的引用。使用软指针,Java的垃圾收集器能够以单独的线程在后台运行,并依次检查每个对象。(标记对象、移除对象、移动对象或检查对象)
java语言中,判断一块内存空间是否符合垃圾收集器手机标准只有两个条件:(1)给对象赋予了null空值以后再也没有调用过;(2)给对象赋了新值,即重新分配了内存空间。
(提醒:一块内存空间符合垃圾收集器的收集标准,并不意味着这块内存空间就一定被垃圾回收器回收。)
垃圾回收注意几点:
1)不要试图假定垃圾回收时间,未知的。
2)Java中提供一些和垃圾回收打交道的方法,而且还提供强制执行垃圾回收的方法--调用System.gc(),但是该芳芳同样是不确定的方法。(并不能保证调用就一定能启动来及回收,只是向JVM发出请求,一切未知)
3)挑选合适的垃圾收集器。(一般使用JVM默认选项,但,,)
4)内存泄漏问题(内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用),对象都是有生命周期的,有的长,有的短,如果长生命周期的对象持有短生命周期的引用,就很可能会出现内存泄露)。良好的编程习惯和严谨的变成态度。
在Java程序中,我们通常使用new为对象分配内存,而这些内存空间都在堆(Heap)上。
此处借用别人的例子:
public class Simple { Object object; public void method1(){ object = new Object(); //...其他代码 } }
method1方法调用结束后object对象没有被释放,一直到Simple结束才被释放。
解决方案:
public class Simple { Object object; public void method1(){ object = new Object(); //...其他代码 object=null //增加一步 } }
5)尽早释放无用对象的引用。对于频繁申请内存和释放内存的操作,最好使用finalize强制执行或者自己写finalize方法,System.gc()方法不一定适用。在jvm垃圾收集器收集一个对象之前,一般要求程序员调用适当的方法释放资源,但在没有明确释放资源的情况下,java提供了默认机制终止化该对象来释放资源,即finalize()方法。
2.2 内存管理
java的内存管理就是对象的分配和释放问题。java中,程序员通过关键字new为每个对象申请内存(基本类型除外),所有对象都在堆中分配空间。对象释放由gc决定和执行。gc监控每一个对象的运行状态(申请、引用、被引用、赋值等)
内存泄露两个特点:1)对象可达,即存在通路与其相连2)对象是无用的。(内存泄露原因:1.全局集合。2.缓存3.ClassLoader)
2.3 clone
3.传递与引用
3.1传值和传引用
在java中,变量分为两类。(java传值传引用的讨论)
1)对于基本数据类型(int float long double boolean char byte),传值的副本。
2)对于一切对象型变量,java都是传引用的副本。传引用副本的实质就是复制指向地址的指针。(这点与C++不同,在c++中当参数是引用类型时,传递的是真实的引用而不是副本)
String类型也是对象类型,所以它必然是传引用副本。它是非可变类,使用传值或者传引用 显得 没有区别。
对于基本类型而言,传值就是把自己复制一份传递,即使自己的副本变了自己也不会变。
对于对象类型,它传的是副本引用,指向自己的地址而不是实际值的副本。因为对象类型放在堆里,一方面速度相对于基本类型较慢,另一方面对象类型本身比较大,如果选择重新复制值浪费内存且速度慢。(有趣比喻:复制钥匙而不是复制仓库) ---说明,一些书籍如thinking in java提到“不管是基本类型还是对象类型,都是传值”他们是把引用副本也当做一种值。
引用传递例子1:
1 public class Test { 2 public static void main(String[] args) { 3 StringBuffer str= new StringBuffer("Hello"); 4 test(str); 5 System.out.println(str); 6 } 7 public static void test(StringBuffer s){ 8 s=s.append(",World!"); 9 } 10 }
输出:Hello,World!
又一个例子2:
1 public class Test { 2 public static void main(String[] args) { 3 String str= "Hello"; 4 test(str); 5 System.out.println(str); 6 } 7 public static void test(String s){ 8 s="World!"; //系统自动生成一个新的String对象设为“World”,然后把这个对象的引用赋给str. 9 } 10 }
输出:Hello
Stringl类是final类型的,因此不可以修改和继承这个类,当例子2中函数结束,s作用消失。
例子1中StringBuffer是产生一块内存空间,相关的增删改操作都在其中,仍然是在同一段内存地址上进行。
String StringBuffer Stringbuilder
1)可变、不可变
String类中使用字符数组保存字符串,因为有“final”修饰符,所以可以知道string对象是不可变的。StringBuffer和Stringbuilder是可变的)
----String -----private final char value[];
-----StringBuffer和Stringbuilder -----char[] value;
2)是否线程安全
String中的对象是不可变的,也就可以理解为常量,显然线程安全。
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。
同理,数组传值的本质也是传地址值的副本。
3.2. 静态变量与私有变量
定义在类里的变量会默认一个初始值。布尔类型默认初始值是false
main函数不能访问一个非静态变量或方法
3.3.输入/输出流
大文件读取:new BufferedReader(new InputStreamReader(new FileInputStream("filename")))
写入文件:FileOutputStream =new FileOutStream("filename") ;out.write("string to write".getBytes());out.close();
java IO操作有面向字节Byte和面向字符Character两种方式.
面向字节的操作以8位二进制为单位对数据操作,不需要对数据转化,这些操作的类都是InputStream和ouputStream的子类。
面向字符的操作以字符为单位读取时需要将二进制转化为字符,写的时候需要将字符转化为二进制,这些类都是Reader和Writer的子类。
3.4 序列化
3.5 HashMap和HashTable区别
Hashtable和HashMap有几个主要的不同:线程安全以及速度。
1)Hashtable是synchronized,HashMap是非synchronized.所以HashTable是线程安全的,HashMap是线程不安全的。因此如果是单线程,HashMap效率比HashTable高。
2)HashMap支持null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
3)HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。