java基础总结(二)

概览:

java基础

数组:

一组数据的集合,是一种引用类型,元素可以是几倍呢类型也可以是引用类型但是只能是同一类型。数组作为对象,数组中的元素作为对象的属性。数组还有一个成员属性length,在创建时确定,无法修改。数组元素有下标[0 - n-1],可以通过下标访问元素。

数组的声明方式:

数组元素类型[] 变量名称:int[] a; Student[] stu;

使用new操作符创建静态数组:

type[] 变量名 = new type[数组中的元素个数],这种方式创建的数组已经动态初始化了,预先在堆内存中分配空间,每一个元素都有默认值(boolean为false,其余为0)

数组重点:

数组是引用类型,它在堆中分配内存空间,传入的是地址,会改变原值。数组通过下标读取修改,从0开始。

数据的内容比较可以使用equals()方法吗?

数组的比较equals使用Object的比较实现,没有重写,不能比较。一种解决方案是自己实现,另一种是用java.util.Arrays工具类。

public staitc boolean isEquals(int[] a,int[] b){
    if(a == null || b == null){ return false;}
    if(a.length != b,length){retrun false;}
    for(int i = 0;i<a.length;i++){
        if(a[i]!=b[i]){return false;}
    }
    return true;
}

数组扩容:

先新建新的数组,将原来的数据拷贝,原数组GC回收,数组拷贝会消耗大量资源。

public static void arrayCopy(int[] src,int srcPos,int[] destPos,int length){
    for(int i = srcPos;i<srcPos+length;i++){
        dest[destPos++] = src[i];
    }
}

数组怎么存放不同类型的数据类型?

数组排序冒泡:

public int[] bubbleSort(int[] array){
    int len = array.length;
    for(int i = array.length-1;i>0;i++){
        for(int j = 0;j<i;j++){
            if(array[j] > array[j+1]){
                swap(array,i,j);
            }
        }
    }
    return  array;
}

数组排序选择排序:

public int[] selectSort(int[] array){
    for(int i = 0;i<array.length-1;i++){
        int min = i;  //记录最小标记位
        for(int j = i+1;j<array.length;j++){
            if(array[i] < min){
                min = j;  //找出最小位置
            }
        }
        swap(array,i,j);  //第一次循环找出第一个最小后交换位置即可
    }
    return array;
}

数字数组二分法查找:

public int binarySearch(int[] array,int target){
    int left = 0;
    int right = array.length;
    Arrays.sort(array);  //二分前一定保证数组是排好序的。
    while(left <= right){
        int mid = (left + right) / 2;
        if(target >  array[mid]){
            left = mid+1;
        }eles if(target < array[mid]){
            right = mid-1;
        }else if(target == array[mid]){
            return arrat[mid];
        }
        return null;
    }
}

常用类:

String:字符串,引用类型,不属于基本数据类型,被final修饰无法继承,声明后不可修改。

public final class String implements java.io.Serializable,Comparable<String>,CharSequebce{}

以上内存图可以看到,String对象赋值后不能再修改,如果修改,就会创建新的对象。

注意:只要双引号赋值的字符串,在编译期.class时将会放在方法区中的字符串常量池中,字符串相加减会放在堆中(放前先验证是否有相同的字符串常量,存在,返回地址,不存在,先将字符串常量放在池中,然后再返回该对象的地址。)

String s1 = "abc";  //方法区常量池
String s2 = "abc";  //方法区常量池
String s3 = new String("abc");  //new时存在双引号,会在常量池中找,所以常量池不再放置,而new会在堆中分配内存,所以堆中会创建一个abc,s3指向abc

sout(s1 == s2);  //true
sout(s2 == s3);  //true
sout(s2.equals(s3));  //s2和s3比较必须用equals,String类已经重写了equals方法
String s1 = "abc"
s1 = "abcd";  //底层创建两个对象,分别是“abc”和“abcd”都放在字符串常量池中。

String s1 = new String("abc");
String s2 = new String("abc");
//创建三个对象,堆2个,方法区1个

记住:堆区中是运行期分配的,常量池是编译期分配的

String常用方法:endsWith,equals,indexOf,lastIndexOf,length,split,substring,trim,valueOf。

使用String时的注意事项:因为String是不可变对象,如果多个字符串进行拼接,将会形成多个对象,这样会造成内存溢出,会给垃圾回收带来工作量,如下面应用最好不要用String。

StringBuffer和StringBuilder:

可变长对象,底层char数组,都有同一个父类AbstractStringBuilder,定义了一些常用方法(expandCapacity,append,insert,indexOf)。StringBuffer所有方法上有sybchronized关键字,StringBuilder类的方法上没有synchronized关键字,两者初始容量都是16,扩容原则是value.length*2+2。

StringBuffer:字符串缓冲区,预先申请一块内存,存放字符序列,如果字符系列满了,会重新改变缓冲区的大小,以容纳更多的字符序列。StiringBuffer是可变对象,这个是String最大的不同

StringBuilder:用法同StringBuffer,StringBuilder和StringBuffer的区别是StringBuffer中所有的方法都是同步的,是线程安全的,但速度慢。Builder相反。

String和StringBuffer,StringBuiler异同点:

1.可变性:String为final不可变,SBuffer和SBuilder继承AbstractStringBuilder可变,其中字符数组保存字符串char[] value,但是没有final关键字。

2.线程安全性:String不可变为常量,安全。SBuffer对方法加同步锁,安全,SBuiler没加不安全。

3.性能:String会创建新的对象,SBuffer不生成新对象,SBuilder能提升,但是不安全。

总结:操作少量数据=String;单线程大量=StringBuilder;多线程大量=StringBuffer。

基本类型对象包装类:

包装类提供更多的操作,且都是final的,不能创建子类,都是不可变对象.(int-Integer,char-character)。

除了boolean和Character外,其他的包装类都有valueOf()和parseXXX方法。

自动拆装箱:JDK5前,包装类和基本类运算时,必须将包装类转换成基本类才可以,而JDK5提供了Auto-boxing,属于编译阶段功能,和运行期无关

String,int和Integer之间的转换:

int a = 10;
Stirng str = "10";
Integer b = new Integer(10);

//String和Integer转换
new Integer(str);  //String->Integer
b.toString();  //Integer->String

//String和int之间的转换
Integer.parseInt(str);  //String->int
a+"";  //int->String

//Integer和int之间的转换
b.intValue();  //Integer->int
new Integer(a);  //int->Integer

数字类:

java.text.DecimalFormat:DecimalFormat用来数字格式化的,如#(任意数字),(千分位).(小数点)0(不够补0)

//加入千分位,保留2位
DecamalFormat df = new DecimalFormat("###,###.##");
//加入千分位保留4位,补0
new DecimalFormat("###,###.0000").format(12345.12);

java.math.BigDecimal:使用BigDecimal可以精确计算,特别是财务数据。如果是电商项目,或者存储价格字段就要用BigDecimal。或者四舍五入问题等。

java.util.Random:Random类实现随机算法的伪随机,就是有规则地随机,起源数字称为种子数(seed),在其基础上进行一定的变化,产生需要的随机数字。相同种子数的Random对象,相同次数生成的随机数字是完全相同的,在生成多个随机数字时需要注意

Random对象的生成:Random包含两种构造方法:public Random():使用当前系统时间对应的数字作为种子数 和public Random(long seed):使用自定义种子数。

Rendom常用方法:nextBoolean();nextDouble();setSeed(long seed);

Enum:

枚举:java5新增,允许常量来表示数据片段,而且全部都以类型安全的形式来表示。本质是类,频闭了枚举值的类型信息,不像用public static final定义变量都必须指定类型。用来构建常量数据结构的模板,可扩展,增强程序健壮性。

枚举自定义函数:枚举构造器只是在构造枚举值的时候被调用,构造器只能私有private,绝不允许public,这样可以保证外部代码无法新构造枚举类的实例,但枚举类的方法和数据域可以允许外部访问

public enum Color{
    RED("red",1),GREEN("green",2);
    
    private String name;
    private int index;

    private Color(String name,int index){
        this.name = name;
        this.index = index;
    }

    //values()方法:静态,返回一个包含全部枚举值的数组
    for(Color color : Color.calues()){
        sout(color+""+color.getIndex());
    }
}

容器集合:

List:有序集合,可以放重复的数据。

Set:无序集合,不允许放重复的数据。

Map:无序集合,集合中包含一个键对象,值对象,键对象不允许重复,值对象可重复(身份证)

Collection,Iterator,Collections:

Collection接口:是List和Set的父接口,在Collections中定义了一些主要的方法。add(),clear(),contains(Object o),isEmpty(),iterator(),remove(),size(),toArray()。

Iterator接口:迭代接口,遍历集合中的数据,主要方法hasNext(),next(),remove()。

List()接口:

主要实现ArrayList和LinkedList,都是有序的线性存储,可以看作是一个可变数组。1.ArrayList:查询快,添加删除慢(基于可变数组)2.LinkedList:查询慢,添加和删除快(基于链表数据结构)3.Vector:同步,效率慢被ArrayList(但不同步)取代 4.Stack是继承Vector实现了一个栈,被LinkedList取代

ArrayList:底层是数组队列,相当于动态数组,与java数组相比,它的容量能动态增长,在添加大量元素前,应用程序可以使用‘ensureCapacity’来增加ArrayList实例的容量。可以减少递增式再分配的数量。ArrayList默认容量是10。每次扩容为原来的1.5倍。若新增超过这个量,则为超过最小值,如果增加0.5倍后的新容量超过限制的容量,则用所需的最小容量与限制的容量进行判断,超过则指定为Integer的最大值,否则指定为限制容量的大小。然后通过数组的复制将原数据复制到一个更大的数组。

public void grow(int minCapacity){
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity+(oldCatacity>>1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if(Capacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    element = Arrays.copyOf(elementData,newCapacity);
}

private static int hugeCapacity(int minCapacity){
    if(minCapacity < 0)
        throw new OutOfMemoryError();
    retiurn (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAXVALUE:MAX_ARRAY_SIZE;
}

LinkList:链表,增删快,查找访问慢,提供List接口中没有定义的方法,用于操作表头和表尾,可以当作堆,栈,队列和双向队列使用。LinkedList实现了List接口和Deque接口的双端链表。线程不安全,想要安全可以调用静态类Collection类中的synchronizedList方法:javaList list = Collections.synchronizedList(new LinkedList(...))

ArrayList和LinkedList异同:

1.是否线程安全:都不安全

2.底层数据结构:ArrayList底层使用Object数组;LinkedLis底层使用双向链表数据结构

3.插入和删除是否受元素位置的影响:ArrayLsit数组,中间插入删除有影响,尾插影响,LinkedList链表,插入删除不受元素位置影响。

4.是否支持快速随机访问:LinkedList不支持高效的随机元素访问,ArrayList支持。

5.占用内存:ArrayList空间浪费为list列表结尾预留一定的容量空间,而LinkedList空间花费在每一个元素都需要消耗比ArrayList多的空间(放置前驱和后继指针)。

ArrayList和Vector区别:线程同步区别(ArrayList使用CopyOnWriteArrayList同步)。

System.arraycopy()和Arrays.copyOf():

相同:都调用了‘System.arraycopy()’方法。

不同:arraycopy()需要目标数组,将原数组拷贝到自己定义的数组,可选拷贝起点和长度。copyOf()是系统自动在内部新建一个数组,并返回该数组

set接口:

哈希表:基于数组,不能扩展。数组中的元素值和下标存在明确的对应关系,通过元素的值就能换算出数据元素的下标,通过下表就可以定位数组元素。这样的数组就是哈希表。

hashSet:无序不可重复,按照哈希算法存储数据,当向HashSet插入数据的时候,会调用对象的hashCode得到该对象的哈希码,然后根据哈希码计算出该对象的插入到集合中的位置。

hashCode()与equals()的相关规定:

1.如果两个对象相等,则hashCode一定也是相同的。

2.两个对象相等,对两个对象分别调用equals方法返回true

3.两个对象有相同的hashcode值,他们也不一定是相等的。

4.equals方法被覆盖过,则hashcode方法也必须被覆盖。

5.hashCode()的默认行为是对堆上的对象产生独特值,如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使两个对象指向相同的数据)

hashCode()(相当于hash算法):该方法是Object中定义的方法,返回int类型,在Object中缺省实现:该方法执行结束后返回值可以“等同”看作是一个java对象的内存地址。这个哈希码的作用是确定该对象在哈希表中的索引位置。

先由hashCode方法确定定位在数组中的哪一个hash桶里面,在由equals方法确定是否同一个对象,如果equals比较相同将不把此元素加入到Set集合中,但equals比较不相等会重新根据hashCode换算位置仍然将该元素加进去。

特别强调:向HashSet和HashMap中加入元素时必须同时覆盖equals和hashCode方法。

java要求:

两个对象equals相等,那么他的hashCode相等。

两个对象equals不相等,那么他的hashCode并不要求它不相等,但一般建议不相等。

hashCode相等不代表两个对象相等(采用equals比较)

TreeSet:可以对Set集合进行排序,默认自然排序,但也可做客户化排序。基本类型的包装类和String类都可以排序,因为他们实现了Comparable接口,但是自定义引用类型必须是可排序的类。

1.实现Comparable接口完成排序:

class Person implements Comparable{
    Stirng name;

    int age;

    //覆盖equals
    public boolean equals(Object obj){
        if(this == obj){
            return true;
        }
        if(obj instanceof Person){
            Person p = (Person)obj;
            return this.name.equals(p.name);
        }
        return false;
    }

    //覆盖hashCode
    public int hashCode(){
        return (name == null)?0:name.hashCode();
    }

    //如果覆盖了equals,最好保证equals和Compareto在相等情况下的比较规则是一致的
    public int compareTo(Object o){
        if(o instanceof Person){
            Person p = (Person)o;
            //升序
            return (this.age - p.age);
            //降序
            return (p.age - this.age);
        }
        throw new IllegaArugmentException("非法参数,o=" +o);
    }
}

2.实现Comparator接口:

Set set = new TreeSet(new PersonComparator());

class PersonComparator implements Comparator{
    @Override
    public int compare(Onject o1,Object o2){
        if(!(o1 instanceof Person) || !(o2 instanceof Person)){
            throw new RuntimeException("参数格式错误");
        }
        return ((Person)o1).age - ((Person)o2).age;
    }
}

3.匿名内部类:

Set set = new TreeSet(new Comparator(){
    @Override
    public int compare(Onject o1,Object o2){
        if(!(o1 instanceof Person) || !(o2 instanceof Person)){
            throw new RuntimeException("参数格式错误");
        }
        return ((Person)o1).age - ((Person)o2).age;
    }
});

Map接口:

Map中存放键值对,较常见的实现为HashMap,对键值对的存取和HashSet一样,采用哈希算法,所以必须重写equals和hashCode方法。

HashMap(数组+链表+红黑树):根据键的hashCode值存储数据,具有快速访问,但遍历顺序不确定,hashMap最多只允许一条记录的键为null,允许多条记录的值为null。hashMap非线程安全,可以用Collections的synchronizedMap方法使其具有线程安全的能力。或者使用ConcurrentHanMap

HashMap是一个数组,然后数组中每个元素都是一个单向链表,Entry包含四个属性:key,value,hash值和单链表的next。java8中链表超过8个元素会转换为红黑树,可将时间复杂度降为O(logN)。

1.capacity:当前数组容量(16),始终保持2^n,可以扩容,扩容为当前的2倍。

2.loadFactor:负载因子,默认为0.75

3.threshold:扩容的阈值,等于capacity*loadFactor

java7和java8最大不同是树化,当bin被映射到同一个桶时,如果这个桶中的bin的数量小于等于TREEIFY_THRESHOLD(默认8)不会转化为树形存储,如果bin大于,但是capacity小于MIN——TREEIFY_CAPACITY(默认64),依然是链式存储,此时只会对HashMap进行扩容,如果capacity对于64,才会树化。

hashMap put元素图:

1.判断键值对数组是否为null,否则执行resize扩容

2.根据键值key计算hash值得到插入数组的索引i,如果table[i]==null,直接新建节点,转向6,如果不为空,转向3

3.判断table[i]的首个元素是否和key一样,如果(hashCode和eqalsl)相同直接覆盖value,否则转4

4.判断table[i]是否为TreeNode,即是否是红黑树,是直接插入键值对,否则转5

5.遍历table,判断链表长度是否大于8,大于8转红黑树插入,否则链表插入,遍历发现key已经存在直接覆盖value即可。

6.插入成功后,判断实际存在的键值对数量size是否查过了最大容量threshold,超过扩容

resize扩容操作:初始化为16,put时当检查到size超过threshold执行resize,如果capacity已经大于最大值,便把threshold置为int最大值,否则对capacity,threshold进行扩容操作发生扩容,必须Map中的所有的数进行再散列,重新装入

jdk7和jdk8中关于HashMap的对比:

1.树化

2.hash值得计算方式不同

3.7中table创建hashMap时分配空间8中put的时候分配,如果table为空,则为table分配空间,在发生冲突,插入链表中时,7是头插,8是尾插

4.在resize操作中,7需要重新进行index计算,8不需要,通过判断相应的位是0还是1,要么依旧是原index,要么是oldCap+原index。

HashMap遍历方式:

public static void main(){
    Map<String,String> map = new HashMap<>();
    map.put("1","value1");
    map.put("2","value2");
    map.put("3","value3");
    
    //第一种,普通方式通过ketSet遍历key和value
    for(String key:map.keySet()){
        Sout(key+map.get(key));
    }

    //第二种,通过map.entrySet()使用Iterator遍历key和value
    Iterator<Map.Entry<String,String>> ite = map.entrySet().iterator();
    while(ite.hasNext()){
        Map.Entry<String,String> entry = ite.next();
        Sout(entry.getKey(),entry.getValue());
    }

    //第三种,推荐尤其是容量大的时候,map.entrySet遍历key和value
    for(Map.Entry<String,String> entry:map.entrySet()){
        Sout(entry.getKey(),entry.getValue());
    }

    //第四种,使用map.value()遍历所有的value但不能遍历key
    for(String v:map.values()){
        Sout(v);
    }
}

HashMap自定义类作为Key:

public void main(){
    IdCard idCard = new IdCard();
    map.put(idCard,person);

    //加入重复数据,因为HashMap底层实现采用hash表,所以Map的key必须覆盖hashCode和equals方法
    for(Iterator ite = map.entrySet().iterator();ite.hasNext()){
        Map.Entry entry = (Map.Entry)ite.next();
        IdCard idCard = (IdCard)entry.getKey();
        Person person = (Person)entry.getValue();
        Sout(idCard.cardNo,person.name);
    }
}

HashMap覆盖key的equals和hashCode方法:

public boolean equals(Object obj){
    if(obj==this){return true;}
    if(obj instance IdCard){
        IdCard idCard = (IdCard)obj;
        if(this.cardNo == idCard.cardNo){return true;}
    }
    return false;
}

public int hashCode(){
    return new Long(cardNo).hashCode();
}

TreeMap:对Map中的key进行排序,如果map中的key采用的是自定义类那么需要实现Comparable或Comparator接口完成排序:Map map = new TreeMap();

LinkedHashMap:是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序

HashMap和HashTable的区别:

1.线程是否安全:HashMap不安全,HashTable安全,内部方法都是synchroized修饰,或者使用ConcurrentHashMap线程安全类。

2.效率:hashMap效率高,HashTable基本被淘汰。

3.对null key和null value支持:HashMap中,null可以作为键,这样键只有一个,可以有多个键对应值为null。但是HashTable中put进的键值只要有一个null,直接抛出空指针异常。

4.初始容量大小和每次扩容容量大小的不同:

*创建时不指定容量初始值,Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量为原来的2倍。

*创建时如果给定了容量初始值,那么HashTable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小(HashMap中的tableSizeFo()方法保证,下面给出源代码)。也就是说HashMap总是使用2的幂作为哈希表的大小,后面会介绍为什么是2的幂次方。

5.底层数据结构:JDK8以后的HashMap在解决哈希冲突时有了较大变化,hashTable没有。

HashSet和HashMap区别:

HashSet底层基于HashMap实现,HashSet就是HashMap的key

ConcurrentHashMap实现原理:由于HashMap是一个线程不安全的容器,主要体现在通量大于总量*负载因子发生扩容时会出现环形链表从而导致死循环。因此需要ConcurrentHashMap。

Segmen段:ConcurrentHashMap由一个个Segment组成。Segment继承ReentrantLock加锁,每次需要锁住Segment,就实现了全局的线程安全。

Collections工具类:

排序:反转reverse(List list),随机排序shuffle(List list),自然排序升序sort(List list),定制排序sort(List list,Comparator c),交换两个索引位置的元素swap(List list,int i,int j),旋转,当distance为正数时,将list后distance个元素移到前面,反之亦然rotate(List list,int distance)

查找,替换:对List进行二分查找,返回索引,注意List必须是有序的binarySearch(List list,Object key),根据元素自然序返回最大max(Collection coll),min(Collection coll),max(Collection coll,根据定制排序返回最大值Comparator c),min(Collection coll,Comparator c),用指定元素代替指定list中所有元素fill(List list,Object obj),统计元素出现次数frequency(Collection c,Object o),用新元素替换旧元素replaceAll(List list,Object oldVal,Object newVal),统计target在list中第一次出现的索引,找不带返回-1indexOfSubList(List list,List target),lastIndexOfSubList(List source,list target)

同步控制:返回支持同步的各个方法sychronizedCollection(Collection<T> c);sychronizedList(List<T> list);sychronizedMap(Map<K,V> m); sychronizedSet(Set<T> s);

Collrctions设置不可变集合:返回一个空的,不可变的集合对象emptyXxx();返回一个只包含指定对象(只有一个或一个元素)的不可变的集合对象,singletonXxx();返回指定集合对象的不可变视图unmodifiableXxx();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值