加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java 基础篇

发布时间:2020-12-15 08:01:48 所属栏目:Java 来源:网络整理
导读:集合类: collection:集合类的父接口,集合长度不固定,数组长度固定 add()-添加元素 addAll()-添加集合元素 clear()-清空处理 contains()-是否包含 equals()-重写obj的方法,判断对象是否相等 hashcode()-重写obj的方法,判断对象的哈希码是否相等 isEmpty

集合类:

  collection:集合类的父接口,集合长度不固定,数组长度固定

    add()-添加元素

    addAll()-添加集合元素

    clear()-清空处理

    contains()-是否包含

    equals()-重写obj的方法,判断对象是否相等

    hashcode()-重写obj的方法,判断对象的哈希码是否相等

    isEmpty()-判空

    iterator()-返回迭代器Iterator,hasNext() 以及next()进行迭代

    remove()-移除某元素

    size()-返回集合元素大小

    toArray()-转为数组

  list:

    add(index,xxx)-指定位置添加元素

    addAll(index,xxx)-指定位置添加集合元素

    get(index)-返回指定位置元素

    indexOf(obj)-某元素在集合中的下标

    lastIndexOf(obj)-某元素在集合中最后出现的下标

    remove(index)-移除指定位置元素

    set(index,obj)-替换指定位置元素

    subList(from,to)-返回当前下标区间元素

    sort(Comparator)-根据排序参数排序

    toArray()-转为数组

arraylist:

  以数组实现。节约空间,但数组有容量限制。超出限制时会增加50%容量,用System.arraycopy()复制到新的数组,因此最好能给出数组大小的预估值。默认第一次插入元素时创建大小为10的数组。按数组下标访问元素—get(i)/set(i,e) 的性能很高,这是数组的基本优势。

  直接在数组末尾加入元素—add(e)的性能也高,但如果按下标插入、删除元素—add(i,e),remove(i),remove(e),则要用System.arraycopy()来移动部分受影响的元素,性能就变差了,这是基本劣势。当增加数据的时候,如果ArrayList的大小已经不满足需求时,那么就将数组变为原长度的1.5倍,之后的操作就是把老的数组拷到新的数组里面。

LinkedList是一个简单的数据结构,与ArrayList不同的是,他是基于链表实现的。

?

hashmap:

  在HashMap中有两个很重要的参数,容量(Capacity)和负载因子(Load factor)

  Capacity就是bucket的大小,Load factor就是bucket填满程度的最大比例。如果对迭代性能要求很高的话,不要把capacity设置过大,也不要把load factor设置过小。(0.75)当bucket中的entries的数目大于capacity*load factor时就需要调整bucket的大小为当前的2倍  

  put函数大致的思路为:

  1. 对key的hashCode()做hash,然后再计算index;
  2. 如果没碰撞直接放到bucket里;
  3. 如果碰撞了,以链表的形式存在buckets后;
  4. 如果碰撞导致链表过长(大于等于TREEIFY_THRESHOLD),就把链表转换成红黑树;
  5. 如果节点已经存在就替换old value(保证key的唯一性)
  6. 如果bucket满了(超过load factor*current capacity),就要resize。

  get函数

  

  1. bucket里的第一个节点,直接命中;
  2. 如果有冲突,则通过key.equals(k)去查找对应的entry

    若为树,则在树中通过key.equals(k)查找,O(logn);

    若为链表,则在链表中通过key.equals(k)查找,O(n)。

HashMap不保证数据有序,LinkedHashMap保证数据可以保持插入顺序,而如果我们希望Map可以保持key的大小顺序的时候,我们就需要利用TreeMap了。

使用红黑树的好处是能够使得树具有不错的平衡性,这样操作的速度就可以达到log(n)的水平了。

?

LinkedHashMap是Hash表和链表的实现,并且依靠着双向链表保证了迭代顺序是插入的顺序。

?

泛型:

通配符:<?> ?上限通配符 <? extends shape> 继承shape的子类或者本身 下限通配符 <? super shape> ?shape的父类或者本身

?

代理模式:

给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。

静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。

目标对象(RealSubject )以及代理对象(Proxy)都实现了主题接口(Subject)。在代理对象(Proxy)中,通过构造函数传入目标对象(RealSubject ),然后重写主题接口(Subject)的request()方法,在该方法中调用目标对象(RealSubject )的request()方法,并可以添加一些额外的处理工作在目标对象(RealSubject )的request()方法的前后。

class Proxy implements InterfaceClass {

  private?InterfaceClass obj

  public Proxy(InterfaceClass obj) {

    this.obj = obj

  }

  public void xxxMethod() {

    prehandle()

    obj.xxxMethod()

    lasthandle()

  }

}
动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。

动态代理是指在运行时动态生成代理类。即,代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。

class Proxy implements InvocationHandle {

  private?InterfaceClass obj

  public Proxy(InterfaceClass obj) {

    this.obj = obj

  }

  public Object invoke(Object proxy,Method method,Object[] args) {

    prehandle()

    Object result = method.invoke(obj,args)

    lasthandle()

    return result

  }

}

?

抽象类和接口类

  1. 抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
  2. 抽象类要被子类继承,接口要被类实现。
  3. 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
  4. 抽象类里可以没有抽象方法。
  5. 接口可以被类多实现(被其他接口多继承),抽象类只能被单继承。
  6. 接口中没有?this?指针,没有构造函数,不能拥有实例字段(实例变量)或实例方法。
  7. 抽象类不能在Java 8 的 lambda 表达式中使用。

?

浅拷贝和深拷贝

浅拷贝是拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

?

序列化: 遵守Serializabel

1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。

3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。

?

异常捕获

try中的return语句已经执行了再去执行finally语句,不过并没有直接返回,而是等finally语句执行完了再返回结果。

finally块中的return语句会覆盖try块或者catch块中的return返回。

catch中的return语句先执行,确定了返回值后再去执行finally块,执行完了catch再返回。

finally块中如果没有return语句,在finally中修改变量,假如改变量本身,则不会影响返回值,假如修改变量内部的某个属性和某个元素,则会影响返回值。

?

多线程:

采用实现Runnable、Callable接口的方式创见多线程时,优势是:

线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

劣势是:

编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

使用继承Thread类的方式创建多线程时优势是:

编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

劣势是:

线程类已经继承了Thread类,所以不能再继承其他父类。

?

锁:

synchronized: 对象锁,也可以用作类的锁

ReentrantLock:?显示的获取锁和释放锁

Lock: 显示的获取锁和释放锁

当一个线程得到一个对象后,再次请求该对象锁时是可以再次得到该对象的锁的。

具体概念就是:自己可以再次获取自己的内部锁。

Java里面内置锁(synchronized)和Lock(ReentrantLock)都是可重入的。

ReentrantLock便是一种公平锁,通过在构造方法中传入true就是公平锁,传入false,就是非公平锁。公平锁可以保证线程按照时间的先后顺序执行,避免饥饿现象的产生。

synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

通过Lock可以通过tryLock/isLocked知道有没有成功获取锁,而synchronized却无法办到。

ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。

可以通过readLock()获取读锁,通过writeLock()获取写锁。

Object类中相关的方法有notify方法和wait方法。因为wait和notify方法定义在Object类中,因此会被所有的类所继承。这些方法都是final的,即它们都是不能被重写的,不能通过子类覆写去改变它们的行为。

当前的线程必须拥有当前对象的monitor,也即lock,就是锁,才能调用wait()方法,否则将抛出异常java.lang.IllegalMonitorStateException。

线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。

要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。

被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁,当前线程会在方法执行完毕后释放锁。

wait()/singal() 类似于 conditon的wait()/singal()

?

一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。因为处理器在进行重排序时是会考虑指令之间的数据依赖性,如果一个指令语句 2必须用到语句 1的结果,那么处理器会保证语句 1会在语句 2之前执行。

?

在多线程中共享变量的时候,如果想要变量立即生效,那么使用volatile修饰符

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读