java复习

来源:互联网 发布:数据库中的mvcc 编辑:程序博客网 时间:2024/05/01 01:34

①java指针

a,通过对java数组的复习,了解到java底层很依靠指针的,几乎到处都是指针。每个声明的变量所存储的都是一个指针,数组,l集合,一般类型引用,都是存储的指针。

补充,集合中只能存放对象,而集合存储的实际是对象的引用即地址。数组可以存储基本类型的值,也可以存放对象的引用即地址。

b,没有多维数组:

Object  [ ] [ ] a = new Object[4][ ];这是一个二维数组,这是动态初始化,数组元素为Null,其实完全可以看成一个长度为4的一维数组,该数组的数组元素是一维数组,没初始化的时候为null,该数组元素是一维数组,而该一维数组没有指向任何内存。

补充 数组的初始化有两种方式:静态初始化,和动态初始化。Object  [ ] [ ] a = new Object[4][ ]是动态初始化,String [] = { "s","b" }这个是静态初始化;

c,正确区分基本类型变量和引用类型变量

基本类型的变量名是变量本身。

引用类型变量的名字是复杂数据的存储地点。

这两种类型的对应存储方式如图6所示。


 


②java类加载器:

根类加载器→扩展类加载器→系统类加载器→用户开发自定义类加载器,当加载一个类的时候:

一,该方法会首先调用 findLoadedClass()方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的 loadClass()方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用 findClass()方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写 loadClass()方法,而是覆写findClass()方法。

二,当加载该类的时候,会首先加载并初始化父类,然后再加载子类。

三,当该Class所依赖的和引用的其他的Class也由该类加载器加载。


补充:具体解释看下面的网址,http://blog.csdn.net/zjlolife/article/details/9110367



③java反射:

1,与反射相关的类:Class.该Class,Method,Constuctor,Filed,Annation,Array,AccessibleObject(设置访问权限,可以免除检查) .

2,Method,Constructor,Field都可以调用setAceessible()方法,来免除检查,因为Method,Constructor,Field都继承AccessibleObject。

3,JDK动态代理:

参考下面网址;http://blog.csdn.net/zjlolife/article/details/8762710


④java泛型:

1,泛型派生子类:在物理上并不存在泛型类,比如List<String>并不存在,而是动态生成的。

注意:子类继承泛型派生类。

A  extends Apple  (√)

A  extends Apple<T> (×)

A  extends Apple<String>  (√)  补充这个时候子类中方法的返回值类型必须是String(将T换成 String)


2,类型通配符

类型通配符的由来:举个例子List<String>与List<Object>在java中是List<String>不是List<Object>的子类,List<?>代表任何类型,虽然List<?>可以指定任何类型的一种,但是却不能添加任何对象到这个未知类型List<?>中,因为无法确定确定List可以的是什么类型。

  补充:可以这样List<? extends Apple>和List<? super Apple>,前者代表上限,后者代表下限。


3,泛型方法

泛型方法的由来:泛型方法可以解决类型通配符带来的问题,不能向 List<?>中添加对象。

例子:public static <T>  T   copy(Collection<? super T>  dest  ,  Collection<T>  src) {   };泛型方法与类型通配符联合使用。


4,泛型构造器与java的菱形语法冲突:

不允许同时泛型构造器和java菱形语法

 class MyClass<E>  

{

  public    <T>    MyClass (T    t) {

  System.out.println("参数为"+t);

}

}

MyClass<String>    myclass =  new <Integer> MyClass<>(5);//这是错的表示。不允许同时泛型构造器和java菱形语法


5,泛型的擦处与转换:

比如这样一个类MyClass<T extends Number>,如果没有为某个泛型指定类型形参,那么类中的T就是Number类型的,这里面有可能导致丢失类型。


6,java不支持泛型数组,因为泛型数组违背了泛型的设计原则:。打个比方如果使用了支持泛型数组,那么编译期间没出警告,那么在运行的时候就不应该包转换类型异常错误。而如果支持了泛型数组,就会出现这种问题,因此java不支持泛型数组。

          //下面是错误的,因为不支持泛型数组
            List<String> [] lsa = new ArrayList<String>[10];  ×

  //支持无上限通配符的泛型数组
    List<?> [] las1 = new ArrayList<?>[10]; √    因为在这种情况下,必须进行强制转换。


 ⑤java线程

1,创建线程的三种方式:

a,继承Thread类创建线程类并重写run方法

b,通过实现Runnable接口来创建线程类,因为Runnable对象可以作为Thread对象的taeget,如:new Thread(Runnable rn,String name).

c,可以通过Callable和Future创建线程,线程的执行体是该Callable接口的call()方法,Future接口来代表Callable接口call()方法的返回值,Future接口有一个实现类FutureTask实现类,该类实现了Futrue接口,也实现了Runnable接口。而创建线程的时候可以通过传入实现Runnable接口的对象来创建,而这个FutureTask就充当了方法执行体call()和Runnable中间的桥梁。

上面b,c两种创建线程的方法较好,可以让多个线程共享同一个target对象,因为通过线程可以传入相同的target对象还可以继承其他类。还可以继承其他类。


2,线程的生命周期以及线程的几种状态:新建,就绪,运行,阻塞。

a,当程序使用new 的时候,此时线程的状态就是新建,当调用start方法的时候就是就绪状态(还没有运行)。什么时候阻塞呢?

b,什么情况下阻塞,看下面几种方法:

sleep()该方法会阻塞线程,不会考虑是否有线程的优先级,调用sleep()的该线程会进入阻塞状态,会给其他线程机会执行。

yield()方法不会阻塞线程,而是让该线程从运行状态进入就绪状态,但是之后优先级高,以及优先级相同的线程才会执行,这就是线程让步。

wait()方法会造成线程暂停,即阻塞。同时该方法会释放同步监视器的锁。

调用阻塞式IO的方法,在方法返回之前会造成当前线程阻塞。

Futrue的接口的get()方法在方法返回之前会造成当前线程阻塞。

当某线程调用join()方法的时候,那么调用该方法的线程会阻塞,直到被join方法加入的Join线程执行完为止。这是thread提供的让一个线程等待另外一个线程完成的方法。


3,每个线程都可以设置优先级,Thread提供了setPriority()方法来设置优先级。


4,同步方法和同步代码块:

a,synchronized作为同步的关键字,同步代码块的同步监视器可以是某对象,而同步方法的同步监视器是this.

b,何时会释放同步监视器锁:

当调用同步监视器对象的wait()方法的时候,则当前线程暂停,并释放同步监视器;当线程执行结束,出现异常会释放同步监视器。

当调用Thread.sleep()和Thread.yield()方法时候不会释放同步监视器锁。

c,同步监视器锁定的是某个具体的对象实例。

d,补充:关于java多线程中同步的问题(两个线程访问同一个实例类的两个同步方法,会不会互相影响)。参考网址:http://blog.csdn.net/fycghy0803/article/details/2182127


5,同步锁Lock

参考下面网址:http://blog.csdn.net/ghsau/article/details/7461369

a,一个Lock对象对应一个Account对象。

b,必须手动释放锁,最好放在finally代码块中。


6,死锁的概念:

当两个线程互相等待对方释放两个不同同步监视器的时候,就会出现死锁。

例如,线程1锁住了资源A,并试图去访问资源B,而此时线程2锁住了资源B,并试图访问资源A,这样线程1和2均锁住了对方需要的资源,并且都在等 待对方持有的资源释放后才能继续运行释放自己持有的资源,死锁发生了,两个线程都因为等待资源被阻塞,假如没有一种手段来解除这种互相等待,可以想象,在 理论上二者都会永远等待下去。


参考网址:http://bofang.iteye.com/blog/727739


7,线程通信

a,如果是采用synchronized来实现线程安全的话,那么此时可以用Object的wait()方法和notify()以及notifyall()来实现线程之间的问题。通过这种可以实现一种功能:举个例子,有两个线程分别代表取钱和存钱的线程,那么如果系统要求只要有当存钱后,才能去取钱,并且不能联系两次重复存钱和取钱。


b,如果是采用Lock同步锁来实现线程安全,那么可以用与Lock关联的Condition对象来实现线程之间的通信,Condition对象有await()方法,signal(),signalAll()。与上面的实现线程通信的方法很类似。Lock与Condition有关联。


c,还可以使用阻塞队列来BlockingQueue来控制线程通信。


8,ThreadLocal

有些时候仅仅只是为了防止读线程之间的共享的冲突,那么这个时候就可以使用ThreadLocal。

注意:就算是同一个对象,该对象有一个ThreadLocal变量,那么这个ThreadLocal在不同线程中的值不一样的。可以参考<<疯狂java讲义>>750页的Account类。




⑥java集合

a,对于java集合,其实这是java比较核心的地方,里面有各种数据结构,动态数组,栈,队列,双端队列。这里先介绍下栈和队列的概念:

栈有栈顶和栈尾,进出(插入和删除)的操作都在栈顶就行,因此是后进先出。

队列有队头和队尾,队头进行取出操作,队尾进出插入操作,因此会出现"先进先出"。


b,集合接口有Cllection和Map;


b1,先看Clloection,里面包刮三大子接口分别是Set,List,Queue。

<1>对于List中大部分都是基于数组实现的(即线性表实现),比较List接口中元素的大小的都是通过equals()方法来实现,在List中有一个remove()方法,该表示删除第一个出现的元素,因此可以看出比较两个对象是否相同是通过equals(),而不需通过hashCode()找到地址再比较。

<2>List接口中常见类有ArrayList,Vector,这两个类区别在与Vector是线程同步的。LinekList也是List的实现类,但是该类不是基于数组实现的,而是通过链表的形式实现的,因此在插入,删除上的性能比其他类要好,但是在随机访问上就差些,该类实现了Deque接口,因此可以当成双端队列来使用,所谓双端队列即可以当成栈又可以当成队列来使用。

<3>List接口的类迭代的时候,是将值传过去,而不是将元素本身传过去,因此迭代的时候无法改变元素本身。但是Iterator()中提供了remove方法可以改变集合,但是Collection.remove()在迭代的时候不能用,会报错。

<4>与Set只提供了一个iterator()方法不同,List还提供了一个listIterator()接口来专门来管理List的迭代,该接口增加了可以向前迭代的操作。


b2,对于Set集合来说,常见实现类有HashSet,,TreeSet,LinkedHashSet。Set集合必须保证无重复。因此我们要关心各种Set集合如何比较元素相等。

<1>Set判断一个对象是否相同,是通过该对象的hashCode()方法来找到该元素在Set集合上的位置,然后通过equals()方法比较是否相同。

<2>TreeSet这个类就不是通过hashCode算法来存储了,而是通过红黑树来维护TreeSet的元素存储位置。因此该元素对象是们只需要关心该对象是否实现了Comparable接口,以及该TreeSet传入怎样一个Comparator接口实现来比较元素对象是否相同,跟equals(0)没有一毛钱的关系。

<3>LinkedHashSet是基于链表实现的,以hashCode算法来存储的Set集合。该集合类型仅仅是通过链表来维护元素的顺序。与HashSet其他方面都差不多。


b3,Queue集合

<1>该接口有个比较标准的实现类,PriorityQueue实现类,仅仅是比较标准而已,因为该队列保存元素的顺序不是按照时间的添加顺序来加入队列的顺序,而是按照元素的大小顺序来排列。该类与TreeSet要求基本相同。

<2>该接口下有个子接口Deque,这是个双端队列,ArrayDeque实现了该接口。如果需要用到栈结构,可以考虑使用ArrayDeque,而不是Stack。

c,性能分析

对于基于链表实现的LinkedList和基于数组实现的线性表ArrayList,LinkedList在迭代方面,插入,删除方面都比ArrayList要好。而ArrayList在随机访问上比LinkedList要好。


d,Map

<1>由于Map中的Key要求需要唯一即不能重复,因此,该Map中的key所组成的集合就是Set了。因此这里Set学好了,那么Map就是浮云了。下面需要了解的是Map中的一些方法以及Map中内部类Map.Entry。

<2>关于Map中判断Key相同的是标准是equals()和hashCode()方法,先通过hashCode找到对象所在位置,然后再用equals()比较是否相同。

然后Map中Value只需要通过equals(),当你调用containValue(),就是通过equals来比较的。

<3>keySet()返回Key对应的Set集合。

      values()返回该Map里所有的value组成的Colletion。即元素可以重复的。

<4>Map.Entry是Map的内部类,该类封装的是一个key-value对,每一个Entry代表一对key-value。下面看API来看此类的用法。

映射项(键-值对)。Map.entrySet 方法返回映射的 collection 视图即Set<Map.Entry<K,V>>,其中的元素属于此类。获得映射项引用的唯一 方法是通过此 collection 视图的迭代器来实现。这些 Map.Entry 对象 在迭代期间有效;更确切地讲,如果在迭代器返回项之后修改了底层映射,则某些映射项的行为是不确定的,除了通过setValue 在映射项上执行操作之外



⑦异常处理


参考以下网址:

http://blog.csdn.net/beidou321/article/details/6499288







⑧IO流以及新IO











⑨网络编程












原创粉丝点击