标签:-o demo 等于 安全 规则 other 类构造 防止 tran
主要可以分为两类:
1)Java中定义了一个接口collection,用来存储一个元素集合
2)另一种是定义了映射(map)用来存储键/值对。
Collection接口为线性表(list)、向量(vector)、栈(stack)、队列(queue)、优先队列(priority queue)以及规则集(set)定义了通用的操作
map(映射)是一个存储“键/值对”集合的容器对象。键很像索引,在List中,索引是整数;在Map中,键可以是任意类型的对象。映射中不能有重复的键,每个键都对于一个值。
ArrayList、LinkedList 都是线程不安全的。vector是线程安全的。
ArrayList:用数组存储元素。这个数组是动态创建的,如果元素个数超过数组容量,就会创建一个更大的新数组,并将当前数组中的所有元素都复制到新数组中。
ArrayList实现java.io.Serializable的方式:当写入到输出流时,先写入“容量”,再依次写入“每一个元素”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”。
ArrayList的克隆函数clone(),即是将全部元素克隆到一个数组中。
LinkedList:LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
vector:向量类,是AbstractList的子类。vector除了包含用于访问和修改向量的同步方法外,与ArrayList是一样的。
stack:栈类,继承自vector,提供了后进先出的数据结构。
Queue:队列,但一般使用双向链表LinkedList进行队列操作,因为它可以高效的在列表两端插入和删除元素。(LinkedList实现了双端队列Deque接口,Deque又继承自Queue接口)
PriorityQueue:优先队列,默认情况下,使用Comparable以元素的自然顺序进行排序。
规则集:HashSet、LinkedHashSet、TreeSet (HashSet与TreeSet都是基于Set接口的实现类。其中TreeSet是Set的子接口SortedSet的实现类)
通常可以通过Collections工具类的synchronizedSet方法来"包装"该Set集合。此操作最好在创建时进行,以防止对Set集合的意外非同步访问。
例如:Set hs = Collections.synchronizedSet(new HashSet());
HashSet:实现了Set接口的具体类。默认初始容量16,负载系数0.75。当元素个数超过了容量与负载系数的乘积,容量就会自动翻倍。
LinkedHashSet:用一个链表实现来扩展HashSet。支持规则集内的元素顺序。
TreeSet:实现了SortedSet接口,SortedSet是Set的一个子接口。
如果需要实现定制排序(我们这实现倒序),则需要在创建TreeSet集合对象时,并提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑:
class Person{ Integer age; public Person(int age){ this.age = age; } @Override public String toString() { return "Person [age=" + age + "]"; } } public class Test { public static void main(String[] args){ //实现定制顺序(倒序排) TreeSet<Person> persons = new TreeSet<Person>(new Comparator<Person>(){ @Override public int compare(Person o1, Person o2) { if(o1.age > o2.age){ return -1; }else if(o1.age == o2.age){ return 0; }else{ return 1; } } }); persons.add(new Person(2)); persons.add(new Person(5)); persons.add(new Person(6)); System.out.println(persons); } } //打印结果为[Person [age=6], Person [age=5], Person [age=2]]
映射:HashMap、LinkedHashMap、TreeMap、ConcurrentHashMap
- 如果更新映射时不需要保持映射中元素的顺序,用HashMap;
- 如果需要保持映射中元素的插入顺序或访问顺序,用LinkedHashMap;
- 如果需要使用映射按照键排序,用TreeMap。
HashMap、LinkedHashMap、TreeMap都是线程不安全的,ConcurrentHashMap是线程安全的。
HashMap :
HashMap的主体是一个数组,数组中的每个元素是一个单向链表,链表的一个节点是嵌套类Entry的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。
Put方法:
HashMap会对null值key进行特殊处理,总是放到table[0]位置。
put过程是先计算key的hash然后通过hash与table.length取模计算index值,然后将键值对放到table[index]位置,当table[index]已存在其它元素时,会在table[index]位置形成一个单向链表,将新添加的元素放在table[index]所对应链表的头部,原来的元素通过Entry的next进行链接,这样以链表形式解决hash冲突问题,当元素数量达到临界值(capactiy*factor)时,则进行扩容,是table数组长度变为table.length*2
get方法:
同样当key为null时会进行特殊处理,在table[0]的链表上查找key为null的元素。
get的过程是先计算key的hash然后通过hash与table.length取摸计算index值,然后遍历table[index]上的链表,直到找到目标值,然后返回。
resize方法:
这个方法实现了非常重要的hashmap扩容,具体过程为:先创建一个容量为table.length*2的新数组,修改临界值,然后把table里面元素计算hash值并使用hash与table.length*2重新计算index放入到新的table里面。
这里需要注意下是用每个元素的hash全部重新计算index,而不是简单的把原table对应index位置元素简单的移动到新table对应位置。
clear方法:
遍历table然后把每个位置置为null,同时修改元素个数为0。
需要注意的是clear方法只会清除里面的元素,并不会重置capactiy。
containsKey和containsValue:
containsKey方法是先计算hash然后使用hash和table.length取模得到index值,遍历table[index]元素查找是否包含key相同的值。
containsValue方法就比较粗暴了,就是直接遍历所有元素直到找到value
Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成。在 Java8 中,当链表中的元素超过了 8 个以后,会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。
ConcurrentHashMap :
HashMap在并发环境下使用中最为典型的一个问题,就是在HashMap进行扩容重哈希时导致Entry链形成环。一旦Entry链中有环,势必会导致在同一个桶中进行插入、查询、删除等操作时陷入死循环。
ConcurrentHashMap允许多个修改(写)操作并发进行,其关键在于使用了锁分段技术,它使用了不同的锁来控制对哈希表的不同部分进行的修改(写),而 ConcurrentHashMap 内部使用段(Segment)来表示这些不同的部分。实际上,每个段就是一个小的哈希表,每个段都有自己的锁(Segment 类继承了 ReentrantLock 类)。这样,只要多个修改(写)操作发生在不同的段上,它们就可以并发进行。
ConcurrentHashMap实现线程安全的关键点:
相较于JDK1.7,在JDK1.8中,对ConcurrentHashMap做了较大的改动,主要有两方面:
参考 https://www.cnblogs.com/be-forward-to-help-others/p/6708130.html
父类的构造方法不会被子类继承,它们只能使用关键字super从子类的构造方法中调用。
调用父类构造方法必须使用关键字super , 调用语法:
super() 或 super(参数) (无参构造或有参构造)
//例如B继承自A,假设B想调用A的有参构造方法A(int b, String s) public B (int a, int b, String s) { super(b, s); //调用父类构造方法 this.a = a; }
注意:虽然父类构造方法不会被子类继承,但创建一个子类对象会调用其父类的构造方法!
例:
package simplejava; //父类 class Super { String s; public Super() { System.out.println("Super"); } } //子类 class Sub extends Super { public Sub() { System.out.println("Sub"); } } public class Q { public static void main(String[] args) { Sub s = new Sub(); } }
输出:
Super
Sub
当一个类继承了某个类时,在子类的构造方法里,super()必须先被调用;如果你没有写,编译器会自动调用super()方法,即调用了父类的默认无参构造方法;
这并不是创建了两个对象,其实只有一个子类Sub对象;之所以需要调用父类的构造方法是因为在父类中,可能存在私有属性需要在其构造方法内初始化;
注意:出现错误信息:Implicit super constructor is undefined for default constructor
对于子类来说,不管是无参构造方法还是有参构造方法,都会默认调用父类的无参构造方法;当编译器尝试在子类中往这两个构造方法插入super()方法时,因为父类没有一个默认的无参构造方法,所以编译器报错。 (在Java中,如果一个类没有定义构造方法,编译器会默认插入一个无参数的构造方法;但是如果一个构造方法在父类中已定义,在这种情况,编译器是不会自动插入一个默认的无参构造方法,这正是以上demo的情况)
要修复这个错误有以下几种选择:
1、在父类手动定义一个无参构造方法:
public Super(){ System.out.println("Super"); }
2、在子类中自己明确写上父类构造方法的调用;如super(value);
这样就不会报错。
3.移除父类中自定义的构造方法。
参考https://www.cnblogs.com/chenpi/p/5486096.html#_label0
在类加载的init阶段,类的类构造器中会收集所有的static块和字段并执行,static块只执行一次,由JVM保证其只执行一次!
public class TestStatic{ public static String name = ""; static{ System.out.println("init ...."); name = "admin"; } public static String getName(){ return name; } public static String getIdAndName(int id){ return id + "---" + name; } public static void main(String[] args) { String name = TestStatic.getName(); String idAndName = TestStatic.getIdAndName(888); System.out.println(name); System.out.println(idAndName); } } 对执行结果分析: 在调用TestStatic类中任何一个方法时,jvm进行类加载,static语句块是在类加载器加载该类的最后阶段进行初始化的。并且只会被初始化一次。 若一次性调用多个方法,则只会执行一次static代码块。 说明:static语句块,不是在实例化的时候被执行的。
static代码块的使用 :
1、项目对某些数据进行初始化,可以在两个地方处理。
第一、就是在项目启动时,加载某个类,对数据进行数据化(如:初始化基础数据或数据库连接池)。
第二、就是在某个工具类中使用static静态代码块,当第一次访问工具类时,就会先进行初始化(只会执行一次),保存到静态全局属性中,当其他类再次访问时,将直接使用初始化数据(如:连接redis数据库,并初始化连接池)。
2、缓存数据
参考https://huangliangbao.iteye.com/blog/2217362
标签:-o demo 等于 安全 规则 other 类构造 防止 tran
原文地址:https://www.cnblogs.com/toria/p/interview2.html