JAVA基础培训总结 by 光远
通过参加本次JAVA基础培训,自己对之前所了解的一些知识有了一次比较系统的学习。回想整个学习过程,对以下印象比较深刻的知识点在课后做了个简单整理和总结。如有不对,请指正。
提到Java语言,在网上讨论较多的是:Java到底是传值还是传引用?
误区:JAVA对于原始类型(primitive type)传值,对于引用类型(reference type)传引用;
正解:JAVA参数传递都是传值的,传入参数都是“源”的一个复制品。
对于简单数据类型理解上基本没问题,对于对象传参的典型示例:
运行结果:
具体过程:
对象在传递给具体的方法时,会复制一个对象的引用给参数,也就是说,这个Object对象当前至少存在2个引用,引用指向的对象是相同的。
调用tricky方法前后的变化过程如下:
即只有方法的引用发生了改变,源的引用将不会变化。
“==”比较对象是否引用了同一个对象,或者比较简单类型变量值是否相等。
Object类的equals()方法用来比较是否一个对象(内存地址比较),在用equals比较具体对象时可以重写。
重写equals需要严格遵循java.lang.Object的规范:自反性、对称性、传递性、 一致性、非空性。改写的一般规则是:
l使用==操作符检查“实参是否为指向对象的一个引用”。
l使用instanceof操作符检查“实参是否为正确的类型”。
l把实参转换到正确的类型 。
l对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值。
改写一个类的equals方法后,两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode方法,它们仅仅是两个对象。因此,违反了“相等的对象必须具有相等的散列码”。因此,当改写equals()的时候,必须要改写hashCode()。
Collection是最基本的集合接口,一个Collection代表一组Object。Collection中除Set不允许包含重复的元素外,其他的都可添加重复元素。
Collection的通用访问方式:
Iterator it = collection.iterator(); // 获得一个迭代子 while(it.hasNext()) { Object obj = it.next(); // 得到下一个元素 }
List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque);LinkedList没有同步方法,可在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
ArrayList实现了可变大小的数组,也是非同步的。Vector非常类似ArrayList,但是Vector是同步的。如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
还有一点需要注意,在迭代Collection和Map的时候,迭代器在调用next方法时,会检查列表是否被修改过,如果被修改 (add、remove等) 过将会抛出ConcurrentModificationException。
泛型除了基本的用法外,有几个误区:
误区1:
List<String> ls = new ArrayList<String>(); //1 List<Object> lo = ls; //2
假设这两行代码是正确的,那么下面的操作:
lo.add(new Object()); String str = ls.get(0);
将导致运行时错误,通过别名lo存取ls时,可以插入任意类型的对象,ls就不再仅仅支持String了。Java编译器消除了这种错误发生的可能性。
第2行将导致编译时刻错误。一般地说,如果Sub是Par的子类,G定义为某种泛型,那么G<Sub>不是G<Par>的子类。
误区2:
void method(List<String> ls) {…} void method(List<Long> ls) {…} void method (List<Object> lo){…} void method (List<?> l) {…}
以上三个方法放在一起并不能构成重载,相反还会出现编译错误。按照错误提示,可以了解到Java针对泛型采取的是erasure拭去法,拭去了参数,不论是List<String>,List<Object>还是List<?>,编译生成的都是同一段代码,只是在适当的地方加上了类型转换。
Java只有一个JVM进程。
进程相比线程而言比较独立,不共享资源和地址空间的;而线程是共享着相同的地址空间和资源,所以多线程之间的数据不安全。
Synchronized:这个关键字用于保护共享数据,每个对象都有一个锁标志,当一个线程访问该对象时,被Synchronized修饰的数据将被“上锁”,阻止其他线程访问。当前线程访问完这部分数据后释放锁标志,其他线程就可以访问了。
volatile : volatile 变量可以被看作是一种 “程度较轻的 synchronized”, 但是不具备原子特性。由于线程变量缓存一致性,允许线程保存共享成员变量的私有拷贝,而且只有当线程进入或者离开同步代码块时,才能与共享变量的原始值对比,但是线程能够自动发现 volatile 变量保存的最新值(子线程始终能拿到主线程的最新值)。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
<!--[if !supportLists]-->ü <!--[endif]-->对变量的写操作不依赖于当前值。
<!--[if !supportLists]-->ü <!--[endif]-->该变量没有包含在具有其他变量的不变式中。
因此,这个变量可以用在一个控制开关的标志上,可以修饰一个变量始终保存多个线程读写后的最新值。
wait():此方法使当前线程暂停执行并释放对象锁标志,让其他线程可以进入Synchronized数据块,当前线程被放入对象等待池中。此方法放弃当前对资源的占有权,等啊等啊,一直等到有人通知,才会运行后面的代码。
notify():此方法被调用后,将唤醒在等待该对象锁的线程;如果没有等待中的线程,则notify()不起作用。但是只有一个线程从wait状态中恢复,然后继续运行wait()后面的语句。
notifyAll()则唤醒所有在等待改对象锁的线程,通知所有的等待中的线程从wait()方法后的语句开始运行。
wait()和notify()、notifyAll()三个方法用于协调多个线程对共享数据的存取,所以必须在Synchronized语句块内使用这三个方法,否则抛出IllegalMonitorStateException。当某个线程试图等待一个自己并不拥有的对象的监控器或者在自己拥有的同时去通知其他线程等待该对象的监控器时便会抛出该异常。
死锁:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,线程被无限期地阻塞,程序不可能正常终止。常见于线程加锁次序、占有并等待的情况。
java.util.concurrent包:
Atomic原子性:volatile仅仅只是解决了读最新的问题,如volatile int i, i++操作涉及到获取i值、修改i值、存储i值(i=i+1),这里的volatile只是解决了保存i值的问题,至于获取和修改i值,没有做到同步(如2个线程读到的i都是1,执行i++后结果仅仅是2,而不是3)。Atomic*中的变量是申明为volatile变量的,这样就保证的变量的写和读是一致的,然后Atomic提供了getAndIncrement方法,该方法对变量的++操作进行了封装,并提供了compareAndSet方法,来完成对单个变量的加锁和解锁操作。
ExecutorService :继承于Executor接口的接口,调用execute()方法执行线程任务。ExecutorService 提供了多个完整的异步任务执行框架,管理任务的排队和安排,提供了管理终止的方法,可以为一个或多个异步任务执行状况而生成 Future,获取线程返回值。其中,shutdown() 方法在终止前允许继续执行之前提交的任务,而 shutdownNow()方法阻止等待任务启动并试图停止当前正在执行的任务。
Executors:提供大多数 Executor 的常见类型和配置的工厂方法,以及使用它们的几种实用工具方法。如:newCachedThreadPool()--创建一个可根据需要创建新线程的无界线程池,可以进行自动线程回收,但是在以前构造的线程可用时将重用它们;newScheduledThreadPool()--创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行;newFixedThreadPool()创建固定大小线程池。关于线程池,比较常用的还有ThreadPoolExecutor,可灵活配置线程池的大小、调度策略以及拒绝策略。
concurrent包下还有较多线程安全的队列、并发Collection,都是比较实用的,。
Apache组织涉及到 Http服务器,编译工具,类库,开发架构,服务器端Java技术,J2EE容器,数据库工具,日志工具,XML解析等诸多领域。其下的Commons项目下包含了很多开源的工具,主要用于解决平时编程经常会遇到的问题,可有效减少重复劳动。
commons-io:主要在IO操作上提供了一系列的工具,包含了日常工作的静态工具类、输入流、输出流、文件过滤器、文件比较器、文件系统监控器。常见的工具类有FileUtils,对文件的读写、拷贝、删除等提供了很便利的方法。如:List<String> readLines(File file);void copyFile(File srcFile, File destFile);void writeStringToFile(File file, String data)。
commons-beanutils:常用的BeanUtils类提供bean的clone、bean之间属性拷贝、获取bean指定属性值等。如:void copyProperties(Object dest, Object orig),String getSimpleProperty(Object bean, String name)。在bean的拷贝使用中,需要注意:如果bean的属性中包含引用类型成员,拷贝之后的bean含有此成员的相同引用,即:拷贝后的bean可以修改拷贝前的bean的引用类型成员。
commons-configuration:用来帮助处理配置文件的,支持很多种存储方式,常见的有:Properties files 、XML documents、JNDI、JDBC Datasource、System properties等。
commons-dbcp:这个是用来配置数据库连接池,实际使用的较多。其BasicDataSource类中提供了连接池这个参数的设置、连接的建立和关闭等。
commons-net:此包封装了很多网络协议,可以各种协议进行编程通信。
其他子项目就不在此列举了,具体的使用可参阅apache官网指导信息。
源码之下,了无秘密。多看开源框架,多看高手写的代码,多看优秀的代码,本身也是一种很好的学习方式。