Java剑开天门(五)

Java剑开天门(五),第1张

Java知识复习五

相关导航
Java剑开天门(一)
Java剑开天门(二)
Java剑开天门(三)
Java剑开天门(四)
Java剑开天门(五)


Java导航
  • Java知识复习五
  • 前言
  • 一、Java类集框架
    • 1、直观框架
    • 2、Collection接口
      • 2.1 List接口
        • 2.1.1 ArrayList
        • 2.1.2 LinkedList
        • 2.1.3 CopyOnWriteArrayList
        • 2.1.4 ArrayList和LinkedList四种遍历方式的总结
      • 2.2 Set接口
        • 2.2.1 HashSet
        • 2.2.2 LinkedHashSet
        • 2.2.3 TreeSet
        • 2.2.4 Queue
        • 2.2.5 遍历方式总结
      • 2.3 Map接口
        • 2.3.1 HashMap
        • 2.3.2 LinkedHashMap
        • 2.3.3 TreeMap
        • 2.3.4 ConcurrentHashMap
        • 2.3.5 遍历方式总结
      • 2.4 集合遍历方式总结
  • 二、泛型
  • 1、泛型类
  • 2、泛型方法
    • 3、泛型通配符 ?
    • 4、泛型限定表达式
    • 5、应用场景
  • 三、IO *** 作
    • 1、直观图
    • 2、字节流和字符流
    • 3、读取文件示例
  • 五、GUI
    • 5.1 Swing组件
    • 5.2JavaFx配置
    • 5.3 JavaFx项目开发
  • 六、总结


前言

本博文重在夯实Java基础的知识点,回归书本,夯实基础,学深学精

近期需要巩固项目和找实习,计划打算将Java基础从头复习一遍,夯实基础,可能对Java的一些机制不了解或者一知半解,正好借此机会深入学习,也算记录自己的一个学习总结。
Java基础参考书籍:《Java 开发实战经典 名师讲坛 第2版》与《Java核心技术 卷I》


 本博文主要归纳整理Java应用程序设计方面的相关知识,如类集泛型 IOGUI相关知识点。

一、Java类集框架 1、直观框架



Java集合框架主要包括两种类型的容器,一种是集合(Collection),另一种是图(Map)Collection接口又有3种子类型,ListSetQueue,再下面是一些抽象类,最后是具体实现类,常用的有ArrayList、LinkedList、HashSet、LinkedHashSet等等。Map常用的有HashMap,LinkedHashMap等。
注意

接口 引用名称=new 具体实现类();//实例化一个实现类对象

正确的,利用了面向对象继承多态思想。

2、Collection接口 2.1 List接口

List接口扩展自Collection,它可以定义一个允许重复有序集合,从List接口中的方法来看,List接口主要是增加了面向位置的 *** 作,允许在指定位置上 *** 作元素,同时增加了一个能够双向遍历线性表的新列表迭代器ListIterator。AbstractList类提供了List接口的部分实现,AbstractSequentialList扩展自AbstractList,主要是提供对链表的支持。

  • 常用方法
    • add(Object element): 向列表的尾部添加指定的元素。

    • size(): 返回列表中的元素个数。

    • get(int index): 返回列表中指定位置的元素,index从0开始。

    • add(int index, Object element): 在列表的指定位置插入指定元素。

    • set(int i, Object element): 将索引i位置元素替换为元素element并返回被替换的元素。

    • clear(): 从列表中移除所有元素。

    • isEmpty(): 判断列表是否包含元素,不包含元素则返回 true,否则返回false。

    • contains(Object o): 如果列表包含指定的元素,则返回 true。

    • remove(int index): 移除列表中指定位置的元素,并返回被删元素。

    • remove(Object o): 移除集合中第一次出现的指定元素,移除成功返回true,否则返回false。

11、iterator(): 返回按适当顺序在列表的元素上进行迭代的迭代器。
下面介绍List接口的两个重要的具体实现类,也是我们可能最常用的类,ArrayListLinkedList

  • 如果除了末尾外不能在其他位置插入或者删除元素,那么ArrayList效率更高,如果需要经常插入或者删除元素,就选择LinkedList
2.1.1 ArrayList

它是用数组存储元素的,这个数组可以动态创建,如果元素个数超过了数组的容量,那么就创建一个更大的新数组,并将当前数组中的所有元素都复制到新数组中。

1、找到add()实现方法。
 
   public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
 
2、此方法主要是确定将要创建的数组大小。
 
  private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
 
        ensureExplicitCapacity(minCapacity);
    }
 
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
 
3、最后是创建数组,可以明显的看到先是确定了添加元素后的大小之后将元素复制到新数组中。
 
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
  • 实例化
ArrayList<String> list = new ArrayList<String>(); //泛型表示集合中存的是字符串形式的元素。
list.add("徐冰"); //add()用于向List集合容器中添加元素。
list.add("萤火");
--------------------
List<String> list = new ArrayList<String>(){{  
     add("string1");  
     add("string2");   
 }};
  • 四种遍历方式
import java.util.*;
 
public class Test{
    public static void main(String[] args){
        List<String> list = new ArrayList<String>();
        list.add("Hello");
        list.add("World");
        list.add("Hello");
 //第一种:withOutSize-for遍历。遍历时,内部不锁定, **效率**最高, 
 //但是当写多线程时要考虑并发 *** 作的问题。
        int size=list.size();
         for(int i = 0;i < size; i ++){
           System.out.println(list.get(i));
       }
 //第二种:withSize-for遍历。遍历时,内部不锁定,集合长度在循环内部,
 // **效率**次于withOutSize-for的方法。
         for(int i = 0;i < list.size(); i ++){
           System.out.println(list.get(i));
       }
       
 //第三种方法: 使用迭代器
 //第三种方法不用担心在遍历过程中超过集合的长度
 //效率次于withSize-for方法
        Iterator<String> ite = list.iterator();
        while(ite.hasNext()){
            System.out.println(ite.next());
        }
//第四种方法: for-each遍历 list   
// 内部调用第三种, 换汤不换药, 
// 因此比Iterator 慢,这种循环方式还有其他限制, 不建议使用它。
//效率最慢
        for(String str : list){         
            System.out.println(str);
        }
          
    }
}

For each其实也是用了迭代器来实现,因此当数据量变大时,两者的效率基本一致。也因为用了迭代器,所以速度上受了影响。不如直接get(index)快

  • 那为何get(index)会比较快呢?

  • 因为ArrayList是通过动态数组来实现的,支持随机访问,所以get(index)是很快的。迭代器,其实也是通过数组名+下标来获取,而且增加了逻辑,自然会比get(index)慢。

2.1.2 LinkedList

LinkedList是在一个链表中存储元素。
链表和数组的最大区别在于它们对元素的存储方式的不同导致它们在对数据进行不同 *** 作时的效率不同
实例化方法同AraayList。

  • LinkedList的四种遍历方式
    LinkedList是通过双向链表实现的,无法支持随机访问。当你要向一个链表取第index个元素时,它需要二分后从某一端开始找,一个一个地数才能找到该元素。这样一想,就能明白为何get(index)如此费时了。
import java.util.*;
 
public class Test{
    public static void main(String[] args){
        List<String> list = new LinkedList<String>();
        list.add("Hello");
        list.add("World");
        list.add("Hello");
 //第一种:withOutSize-for遍历。遍历时,内部不锁定, **效率**最低, 
        int size=list.size();
         for(int i = 0;i < size; i ++){
           System.out.println(list.get(i));
       }
 //第二种:withSize-for遍历。遍历时,内部不锁定,集合长度在循环内部,
 // **效率**高于withOutSize-for的方法。
         for(int i = 0;i < list.size(); i ++){
           System.out.println(list.get(i));
       }
       
 //第三种方法: 使用迭代器
 //第三种方法不用担心在遍历过程中超过集合的长度
 //效率最高
        Iterator<String> ite = list.iterator();
        while(ite.hasNext()){
            System.out.println(ite.next());
        }
//第四种方法: for-each遍历 list   
// 内部调用第三种, 换汤不换药, 
// 因此比Iterator 慢,这种循环方式还有其他限制, 不建议使用它。
//效率次于迭代器的方法
        for(String str : list){         
            System.out.println(str);
        }
          
    }
}
2.1.3 CopyOnWriteArrayList

CopyOnWriteArrayList,是一个线程安全的List接口的实现,它使用了ReentrantLock锁来保证在并发情况下提供高性能的并发读取。

2.1.4 ArrayList和LinkedList四种遍历方式的总结
  • 对于ArrayList和LinkedList,在size小于1000时,每种方式的差距都在几ms之间,差别不大,选择哪个方式都可以。

  • 对于ArrayList,无论size是多大,差距都不大选择哪个方式都可以

  • 对于LinkedList,当size较大时,建议使用迭代器for-each的方式进行遍历,否则效率会有较明显的差距。

所以,综合来看,建议使用for-each,代码简洁,性能也不差。

2.2 Set接口

Set接口扩展自Collection,它与List的不同之处在于,规定Set的实例不包含重复的元素。
在一个规则集内,一定不存在两个相等的元素。AbstractSet是一个实现Set接口的抽象类,Set接口有三个具体实现类,分别是散列集HashSet链式散列集LinkedHashSet和树形集TreeSet

2.2.1 HashSet
  • 散列集HashSet是一个用于实现Set接口的具体类,可以使用它的无参构造方法来创建空的散列集,也可以由一个现有的集合创建散列集
  • 在散列集中,有两个名词需要关注,初始容量客座率
    客座率是确定在增加规则集之该规则集的饱满程度,当元素个数超过了容量与客座率的乘积时,容量就会自动翻倍
  • 集合元素值可以为null
  • 不保证元素的排列顺序,有可能排列顺序与添加顺序不同
  • 非同步集合,多线程访问HashSet时,是不安全的,需要通过同步代码保证同步。
  • 元素不可重复相同,通过equals()和hashCode()方法一起判断是否相同
  • 当我们查询元素时,也是通过该元素的hashCode值快速定位到该元素在集合中的位置,其实HashSet底层是通过HashMap实现的。
    实例化与List相同
  • 两种遍历方式
HashSet中同样不能使用for, 基本相同,for each(内置迭代器)较不稳定

import java.util.*;
 
public class Test{
    public static void main(String[] args){
        HashSet<String> set = new HashSet<String>();
        set.add("Hello");
        set.add("World");
        set.add("Hello");
       
 //第一种方法: 使用迭代器
 //第一种方法不用担心在遍历过程中超过集合的长度
 //效率相当,但稳定
        Iterator<String> ite = set.iterator();
        while(ite.hasNext()){
            System.out.println(ite.next());
        }
//第二种方法: for-each遍历 list   
 //效率相当,但不稳定
        for(String str : set){         
            System.out.println(str);
        }
          
    }
}
2.2.2 LinkedHashSet

LinkedHashSet是用一个链表实现来扩展HashSet类,它支持对规则集内的元素排序【有序】【不可重复】。HashSet中的元素是没有被排序的,而LinkedHashSet中的元素可以按照它们插入规则集的顺序提取
实例化与List相同

LinkedHashSet<String> linkedHashSet =new LinkedHashSet<String>();
		linkedHashSet.add("a");
		linkedHashSet.add("a");
		linkedHashSet.add("a");
		linkedHashSet.add("b");
		linkedHashSet.add("b");
		linkedHashSet.add("c");
		linkedHashSet.add("d");
		
		System.out.println(linkedHashSet);
----------------------------------------
输出
[a,b,c,d]

遍历方式及效率与HashSet相同

2.2.3 TreeSet

TreeSet扩展自AbstractSet,并实现了NavigableSet,AbstractSet扩展自AbstractCollection,树形集是一个有序的Set,其底层是一颗树,这样就能从Set里面提取一个有序序列了
【简而言之,TresSet本身是可以自动排序的】

实例化TreeSet时,我们可以给TreeSet指定一个比较器Comparator来指定树形集中的元素顺序。树形集中提供了很多便捷的方法。
使用Comparator的三种方法

//第一种方法
//类实现Comparable接口,并重写compareTo方法
public class Person implements Comparable{
	private int age;
	private String name;
	public Person(){}

	public Person(int age, String name) {
	    this.age = age;
	    this.name = name;
	}

	public int getAge() {
	    return age;
	}
	
	public String getName() {
	    return name;
	}
	
	public void setAge(int age) {
	    this.age = age;
	}
	public void setName(String name) {
	    this.name = name;
	}
	
	@Override
	public int compareTo(Object o) {
	    if (!(o instanceof Person))
	        throw new RuntimeException("不是人对象");
	    Person p = (Person) o;
	    if (this.age > p.age)
	        return 1;
	    if (this.age == p.age){
	        return this.name.compareTo(p.name);
	    }
	    return -1;
	}
}

----------------------------------------
//第二种方法
//写个类实现Comparartor接口,重写compare方法
 class myComparator implements Comparator{

    @Override
    public int compare(Object o1, Object o2) {
        Person p1 = (Person) o1;
        Person p2 = (Person) o2;

        int num = p1.getName().compareTo(p2.getName());
      // 0的话是两个相同,进行下一个属性比较
        if (num == 0){
            return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
        }

        return num;
    }
}
//在new Set的时候放进去
TreeSet ts = new TreeSet(new myComparator());
----------------------------------------
//第三种方法
//写匿名内部类,重写compare方法
TreeSet ts = new TreeSet(new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            Person p1 = (Person) o1;
            Person p2 = (Person) o2;

            int num = p1.getName().compareTo(p2.getName());

            if (num == 0){
                return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
            }

            return num;

        }
    });

实例化和遍历方式同HashSet

2.2.4 Queue

队列是一种先进先出的数据结构,元素在队列末尾添加,在队列头部删除。Queue接口扩展自Collection,并提供插入、提取、检验等 *** 作。

主要方法
//非阻塞队列
add(E e) : 将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则会抛出异常;
remove() :移除队首元素,若移除成功,则返回true;如果移除失败(队列为空),则会抛出异常;
offer(E e) :将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则返回falsepoll() :移除并获取队首元素,若成功,则返回队首元素;否则返回nullpeek() :获取队首元素,若成功,则返回队首元素;否则返回null
对于非阻塞队列,一般情况下建议使用**offer、poll和peek**三个方法,
不建议使用add和remove方法。
因为使用offer、poll和peek三个方法可以通过返回值判断 *** 作成功与否,
而使用add和remove方法却不能达到这样的效果。
注意,非阻塞队列中的方法都没有进行同步措施。

//阻塞队列(ArrayBlockingQueue、LinkedBlockingQueue、
//PriorityBlockingQueue、DelayQueue)
阻塞队列包括了非阻塞队列中的大部分方法,
上面列举的5个方法在阻塞队列中都存在,
但是要注意这5个方法在阻塞队列中都进行了同步措施。
特有的方法
put(E e) : 用来向队尾存入元素,如果队列满,则等待;
take() : 用来从队首取元素,如果队列为空,则等待;
offer(E e,long timeout, TimeUnit unit) : 用来向队尾存入元素,如果队列满,则等待一定的时间,当时间期限达到时,如果还没有插入成功,则返回false;否则返回truepoll(long timeout, TimeUnit unit) : 用来从队首取元素,如果队列空,则等待一定的时间,当时间期限达到时,如果取到,则返回null;否则返回取得的元素;


添加元素和以上三个Set不同

Queue<Integer> q =  new LinkedBlockingQueue<Integer>(); 
                 //初始化队列 
for (int i = 0; i < 5; i++) { 
         q.offer(i); 
 } 
 //使用offer()方法实现数据添加

实例化和遍历

import java.util.Queue; 
import java.util.concurrent.LinkedBlockingQueue; 

/** 
* 队列的遍历 
* 
* @author leizhimin 2009-7-22 15:05:14 
*/ 
public  class TestQueue { 
         public  static  void main(String[] args) { 
                Queue<Integer> q =  new LinkedBlockingQueue<Integer>(); 
                 //初始化队列 
                 for ( int i = 0; i < 5; i++) { 
                        q.offer(i); 
                } 
                System.out.println( "-------1-----"); 
                 //集合方式遍历,元素不会被移除 
                 for (Integer x : q) { 
                        System.out.println(x); 
                } 
                System.out.println( "-------2-----"); 
                //迭代器遍历
                Iterator it = queue.iterator();
		        while (it.hasNext()) {
		            System.out.println(it.next());
		        }
		        System.out.println( "-------3-----"); 
                 //队列方式遍历,元素逐个被移除 
                 while (q.peek() !=  null) { 
                        System.out.println(q.poll()); 
                } 
                System.out.println( "-------4-----"); 
                while (!queue.isEmpty()) {
                       System.out.println(queue.poll());
                }
        } 
}
总结,与以上三种的遍历方式基本相同,但增加了两种特有的遍历方式
2.2.5 遍历方式总结

建议使用for each遍历,效率和显式迭代器遍历差不多,但是可以和List对应起来,便于记忆。

2.3 Map接口

Map *** 作的是一对对象,就是我们常说的"键值对",Map中每个元素都用key-value的形式存储。
常用方法

添加、删除、修改 *** 作:
Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中

void putAll(Map m):将m中的所有key-value对存放到当前map中

Object remove(Object key):移除指定key的key-value对,并返回value

void clear():清空当前map中的所有数据

元素查询的 *** 作:
Object get(Object key):获取指定key对应的value

boolean containsKey(Object key):是否包含指定的key

boolean containsValue(Object value):是否包含指定的value

int size():返回map中key-value对的个数

boolean isEmpty():判断当前map是否为空

boolean equals(Object obj):判断当前map和参数对象obj是否相等

元素视图 *** 作的方法:
Set keySet():返回所有key构成的Set集合

Collection values():返回所有value构成的Collection集合

Set entrySet():返回所有key-value对构成的Set集合


总结
添加:put(Object key,Object value)
删除:remove(Object key)
修改:put(Object key,Object value)
查询:get(Object key)
长度:size()
遍历:keySet() / values() / entrySet()
2.3.1 HashMap

HashMap是基于哈希表的Map接口的非同步实现,继承自AbstractMap,AbstractMap是部分实现Map接口的抽象类。元素是无序的。

JDK1.8之后,(之前是数组+链表)HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

  • 五种遍历方法
1.使用 Iterator 遍历 HashMap EntrySet
2.使用 Iterator 遍历 HashMap KeySet
3.使用 For-each 循环迭代 HashMap
4.使用 Lambda 表达式遍历 HashMap
5.使用 Stream API 遍历 HashMap
----------------------------------------
1. 使用 Iterator 遍历 HashMap EntrySet
package com.java.tutorials.iterations;  
 
import java.util.HashMap;  
import java.util.Iterator;  
import java.util.Map;  
import java.util.Map.Entry;  
 
/**  
 * 在 Java 中遍历 HashMap 的5种最佳方法  
 *  
 */  
public class IterateHashMapExample {  
    public static void main(String[] args) {  
        // 1. 使用 Iterator 遍历 HashMap EntrySet  
        Map < Integer, String > coursesMap = new HashMap < Integer, String > ();  
        coursesMap.put(1, "C");  
        coursesMap.put(2, "C++");  
        coursesMap.put(3, "Java");  
        coursesMap.put(4, "Spring Framework");  
        coursesMap.put(5, "Hibernate ORM framework");  
 
        Iterator < Entry < Integer, String >> iterator = coursesMap.entrySet().iterator();  
        while (iterator.hasNext()) {  
            Entry < Integer, String > entry = iterator.next();  
            System.out.println(entry.getKey());  
            System.out.println(entry.getValue());  
        }  
    }  
}
 
2. 使用 Iterator 遍历 HashMap KeySet
package com.java.tutorials.iterations;  
 
import java.util.HashMap;  
import java.util.Iterator;  
import java.util.Map;  
 
/**  
 * 在 Java 中遍历 HashMap 的5种最佳方法  
 *  
 */  
public class IterateHashMapExample {  
    public static void main(String[] args) {  
        Map < Integer, String > coursesMap = new HashMap < Integer, String > ();  
        coursesMap.put(1, "C");  
        coursesMap.put(2, "C++");  
        coursesMap.put(3, "Java");  
        coursesMap.put(4, "Spring Framework");  
        coursesMap.put(5, "Hibernate ORM framework");  
 
        // 2. 使用 Iterator 遍历 HashMap KeySet  
        Iterator < Integer > iterator = coursesMap.keySet().iterator();  
        while (iterator.hasNext()) {  
            Integer key = iterator.next();  
            System.out.println(key);  
            System.out.println(coursesMap.get(key));  
        }  
    }  
}
 
3. 使用 For-each 循环遍历 HashMap
package com.java.tutorials.iterations;  
 
import java.util.HashMap;  
import java.util.Map;  
 
/**  
 * 在 Java 中遍历 HashMap 的5种最佳方法  
 *  
 */  
public class IterateHashMapExample {  
    public static void main(String[] args) {  
        Map < Integer, String > coursesMap = new HashMap < Integer, String > ();  
        coursesMap.put(1, "C");  
        coursesMap.put(2, "C++");  
        coursesMap.put(3, "Java");  
        coursesMap.put(4, "Spring Framework");  
        coursesMap.put(5, "Hibernate ORM framework");  
 
        // 3. 使用 For-each 循环遍历 HashMap  
        for (Map.Entry < Integer, String > entry: coursesMap.entrySet()) {  
            System.out.println(entry.getKey());  
            System.out.println(entry.getValue());  
        }  
    }  
}
 
4. 使用 Lambda 表达式遍历 HashMap
package com.java.tutorials.iterations;  
 
import java.util.HashMap;  
import java.util.Map;  
 
/**  
 * 在 Java 中遍历 HashMap 的5种最佳方法  
 *  
 */  
public class IterateHashMapExample {  
    public static void main(String[] args) {  
        Map < Integer, String > coursesMap = new HashMap < Integer, String > ();  
        coursesMap.put(1, "C");  
        coursesMap.put(2, "C++");  
        coursesMap.put(3, "Java");  
        coursesMap.put(4, "Spring Framework");  
        coursesMap.put(5, "Hibernate ORM framework");  
 
        // 4. 使用 Lambda 表达式遍历 HashMap  
        coursesMap.forEach((key, value) -> {  
            System.out.println(key);  
            System.out.println(value);  
        });  
    }  
}
 
5. 使用 Stream API 遍历 HashMap
package com.java.tutorials.iterations;  
 
import java.util.HashMap;  
import java.util.Map;  
 
/**  
 * 在 Java 中遍历 HashMap 的5种最佳方法  
 *  
 */  
public class IterateHashMapExample {  
    public static void main(String[] args) {  
        Map < Integer, String > coursesMap = new HashMap < Integer, String > ();  
        coursesMap.put(1, "C");  
        coursesMap.put(2, "C++");  
        coursesMap.put(3, "Java");  
        coursesMap.put(4, "Spring Framework");  
        coursesMap.put(5, "Hibernate ORM framework");  
 
        // 5. 使用 Stream API 遍历 HashMap  
        coursesMap.entrySet().stream().forEach((entry) - > {  
            System.out.println(entry.getKey());  
            System.out.println(entry.getValue());  
        });  
    }  
}

//两个 entrySet 的性能相近,并且执行速度最快,
//接下来是 stream ,然后是两个 keySet,性能最差的是 KeySet 
// entrySet 的性能比 keySet 的性能高出了一倍之多,
//因此我们应该尽量使用 entrySet 来实现 Map 集合的遍历。
2.3.2 LinkedHashMap

LinkedHashMap继承自HashMap,它主要是用链表实现来扩展HashMap类,HashMap中条目是没有顺序的,但是在LinkedHashMap中元素既可以按照它们插入图的顺序排序,也可以按它们最后一次被访问的顺序排序
LinkedHashMap是通过比HashMap多了一个双向链表实现的有序
遍历方式基本上与HashMap相同。但是有按照何种顺序遍历的分类。

  • 按照插入顺序遍历
  • 按照访问顺序遍历

是否使用访问顺序遍历,是通过LinkedHashMap 的accessOrder参数控制的,true访问顺序遍历,false插入顺序遍历。
设置该值只能在创建LinkedHashMap 时通过构造方法设置的。

/**
		 * 实例化一个LinkedHashMap;
		 * 
		 * LinkedHashMap的插入顺序和访问顺序;
		 * LinkedHashMap(int initialCapacity, 
		 * float loadFactor, boolean accessOrder);
		 * 说明:
		 * 	当accessOrder为true时表示当前数据的插入读取顺序为访问顺序;
		 * 	当accessOrder为false时表示当前数据的插入读取顺序为插入顺序;
		 */
		Map<String,String> linkedHashMap = new 
		LinkedHashMap<String,String>(0,1.6f,true); // 访问顺序; 
//		Map linkedHashMap = new 
//LinkedHashMap(0,1.6f,false); // 插入顺序;

2.3.3 TreeMap

TreeMap基于红黑树数据结构的实现,键值可以使用Comparable或Comparator接口排序。TreeMap继承自AbstractMap,同时实现了接口NavigableMap,而接口NavigableMap则继承自SortedMap。
SortedMap是Map的子接口,使用它可以确保图中的条目排好序的。
TreeMap 默认排序规则:按照key的字典顺序来排序(升序
当然,也可以自定义排序规则:要实现Comparator接口


在实际使用中,如果更新图时不需要保持图中元素的顺序,就使用HashMap,如果需要保持图中元素的插入顺序或者访问顺序,就使用LinkedHashMap,如果需要使图按照键值排序,就使用TreeMap


2.3.4 ConcurrentHashMap

Concurrent,并发,从名字就可以看出来ConcurrentHashMap是HashMap的线程安全版。同HashMap相比,ConcurrentHashMap不仅保证了访问的线程安全性,而且在效率上与HashTable相比,也有较大的提高

2.3.5 遍历方式总结

对照其它集合,建议使用 for each 循环迭代 HashMap的entrySet进行Map的遍历。

for (Map.Entry < Integer, String > entry: coursesMap.entrySet()) {  
            System.out.println(entry.getKey());  
            System.out.println(entry.getValue());  
}  
2.4 集合遍历方式总结

从熟练和效率考虑,Collection和Map集合都建议利用for each内置迭代器进行集合遍历,其中,Map系列利用for each遍历entrySet

二、泛型

泛型的本质是参数化类型,也就是说所 *** 作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
Java语言引入泛型的好处是安全简单。主要针对类型转换

对于常见的泛型模式,推荐的名称是:

 

K ——键,比如映射的键。

V ——值,比如 ListSet 的内容,或者 Map 中的值。

E ——异常类。

T ——泛型。
1、泛型类

泛型类中的类型参数几乎可以用于任何可以使用接口名、类名的地方。

public interface Map<K, V> {

public void put(K key, V value);

public V get(K key);

}
2、泛型方法

泛型方法使得该方法能独立于类产生变化
以下是一个基本的指导原则:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,
因为它可以使事情更清楚明白。另外,对于一个static的方法而言,无法访问泛型类的类型参数。所以,如果static方法需要使用泛型能力,就必须使其成为泛型方法。
定义泛型方法,只需将泛型参数列表置于返回值之前

示例
package Generics;
 
public class GenericMethods {
//当方法 *** 作的引用数据类型不确定的时候,可以将泛型定义在方法上
	public <T> void f(T x){
		System.out.println(x.getClass().getName());
	}
	public static void main(String[] args) {
		GenericMethods gm = new GenericMethods();
		gm.f(99);
		gm.f("掌上洪城");
		gm.f(new Integer(99));
		gm.f(18.88);
		gm.f('a');
		gm.f(gm);
	}
}
/* 输出结果:
java.lang.Integer
java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Character
Generics.GenericMethods
 */

泛型方法与可变参数列表能很好地共存

package Generics;
 
import java.util.ArrayList;
import java.util.List;
 
public class GenericVarargs {
	public static <T> List<T> makeList(T... args){
		List<T> result = new ArrayList<T>();
		for(T item:args)
			result.add(item);
		return result;		 
	}
	public static void main(String[] args) {
		List ls = makeList("A");
		System.out.println(ls);
		ls = makeList("A","B","C");
		System.out.println(ls);
		ls = makeList("ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""));
		System.out.println(ls);
	}
}
/*
[A]
[A, B, C]
[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]
*/

静态方法上的泛型
静态方法无法访问类上定义的泛型。如果静态方法 *** 作的引用数据类型不确定的时候,必须要将泛型定义在方法上。

public static <Q> void function(Q t) {

        System.out.println("function:"+t);

    }
3、泛型通配符 ?

可以解决当具体类型不确定的时候,这个通配符就是?
当 *** 作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型

Class<?>classType = Class.forName("java.lang.String");
4、泛型限定表达式
  • 上限:?extends E:可以接收E类型或者E的子类型对象。
  • ?super E:可以接收E类型或者E的父类型对象。

上限什么时候用:往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类型对象。为什么?因为取的时候,E类型既可以接收E类对象,又可以接收E的子类型对象
下限什么时候用:当从集合中获取元素进行 *** 作的时候,可以用当前元素的类型接收,也可以用当前元素的父类型接收

5、应用场景

当接口、类及方法中的 *** 作的引用数据类型不确定的时候,以前用的Object来进行扩展的,现在可以用泛型来表示。这样可以避免强转的麻烦。

泛型的细节

  • 泛型到底代表什么类型取决于调用者传入的类型,如果没传,默认是Object类型

  • 使用带泛型的类创建对象时,等式两边指定的泛型必须一致;

    原因:编译器检查对象调用方法时只看变量,然而程序运行期间调用方法时就要考虑对象具体类型了;

  • 等式两边可以在任意一边使用泛型,在另一边不使用(考虑向后兼容)。

三、IO *** 作 1、直观图

2、字节流和字符流
  • 字节流可以处理所有类型的数据,如MP3、图片、文字、视频等。在读取时,读到一个字节就返回一个字节。
    在Java中对应的类都以“Stream”结尾
  • 字符流仅能够处理纯文本数据,如txt文本等。在读取时,读到一个或多个字节,先查找指定的编码表,然后将查到的字符返回。
    在Java中对应的类都以“Reader”或“Writer”结尾
3、读取文件示例
public class ReadFromFile {
    /**
     * 以字节为单位读取文件,常用于读**二进制文件**,如图片、声音、影像等文件。
     */
    public static void readFileByBytes(String fileName) {
        File file = new File(fileName);
        InputStream in = null;
        try {
            System.out.println("以字节为单位读取文件内容,一次读一个字节:");
            // 一次读一个字节
            in = new FileInputStream(file);
            int tempbyte;
            while ((tempbyte = in.read()) != -1) {
                System.out.write(tempbyte);
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        try {
            System.out.println("以字节为单位读取文件内容,一次读多个字节:");
            // 一次读多个字节
            byte[] tempbytes = new byte[100];
            int byteread = 0;
            in = new FileInputStream(fileName);
            ReadFromFile.showAvailableBytes(in);
            // 读入多个字节到字节数组中,byteread为一次读入的字节数
            while ((byteread = in.read(tempbytes)) != -1) {
                System.out.write(tempbytes, 0, byteread);
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                }
            }
        }
    }
 
    /**
     * 以字符为单位读取文件,常用于读文本,数字等类型的文件
     */
    public static void readFileByChars(String fileName) {
        File file = new File(fileName);
        Reader reader = null;
        try {
            System.out.println("以字符为单位读取文件内容,一次读一个字节:");
            // 一次读一个字符
            reader = new InputStreamReader(new FileInputStream(file));
            int tempchar;
            while ((tempchar = reader.read()) != -1) {
                // 对于windows下,\r\n这两个字符在一起时,表示一个换行。
                // 但如果这两个字符分开显示时,会换两次行。
                // 因此,屏蔽掉\r,或者屏蔽\n。否则,将会多出很多空行。
                if (((char) tempchar) != '\r') {
                    System.out.print((char) tempchar);
                }
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            System.out.println("以字符为单位读取文件内容,一次读多个字节:");
            // 一次读多个字符
            char[] tempchars = new char[30];
            int charread = 0;
            reader = new InputStreamReader(new FileInputStream(fileName));
            // 读入多个字符到字符数组中,charread为一次读取字符数
            while ((charread = reader.read(tempchars)) != -1) {
                // 同样屏蔽掉\r不显示
                if ((charread == tempchars.length)
                        && (tempchars[tempchars.length - 1] != '\r')) {
                    System.out.print(tempchars);
                } else {
                    for (int i = 0; i < charread; i++) {
                        if (tempchars[i] == '\r') {
                            continue;
                        } else {
                            System.out.print(tempchars[i]);
                        }
                    }
                }
            }
 
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }
 
    /**
     * 以行为单位读取文件,常用于读面向行的格式化文件
     */
    public static void readFileByLines(String fileName) {
        File file = new File(fileName);
        BufferedReader reader = null;
        try {
            System.out.println("以行为单位读取文件内容,一次读一整行:");
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            int line = 1;
            // 一次读入一行,直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                // 显示行号
                System.out.println("line " + line + ": " + tempString);
                line++;
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }
 
    /**
     * 随机读取文件内容
     */
    public static void readFileByRandomAccess(String fileName) {
        RandomAccessFile randomFile = null;
        try {
            System.out.println("随机读取一段文件内容:");
            // 打开一个随机访问文件流,按只读方式
            randomFile = new RandomAccessFile(fileName, "r");
            // 文件长度,字节数
            long fileLength = randomFile.length();
            // 读文件的起始位置
            int beginIndex = (fileLength > 4) ? 4 : 0;
            // 将读文件的开始位置移到beginIndex位置。
            randomFile.seek(beginIndex);
            byte[] bytes = new byte[10];
            int byteread = 0;
            // 一次读10个字节,如果文件内容不足10个字节,则读剩下的字节。
            // 将一次读取的字节数赋给byteread
            while ((byteread = randomFile.read(bytes)) != -1) {
                System.out.write(bytes, 0, byteread);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (randomFile != null) {
                try {
                    randomFile.close();
                } catch (IOException e1) {
                }
            }
        }
    }
 
    /**
     * 显示输入流中还剩的字节数
     */
    private static void showAvailableBytes(InputStream in) {
        try {
            System.out.println("当前字节输入流中的字节数为:" + in.available());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        String fileName = "C:/temp/newTemp.txt";
        ReadFromFile.readFileByBytes(fileName);
        ReadFromFile.readFileByChars(fileName);
        ReadFromFile.readFileByLines(fileName);
        ReadFromFile.readFileByRandomAccess(fileName);
    }
}
五、GUI

图形化的用户界面(Graphical User Interface,简称GUI),Java提供了Swing组件和JavaFx进行GUI开发。
前者是通过代码进行GUI绘制与事件注册;
后者是通过拖拽进行布局。
重点介绍JavaFx这一组件。

5.1 Swing组件
  • java.awt 包 – 主要提供字体/布局管理器

  • javax.swing 包[商业开发常用] – 主要提供各种组件(窗口/按钮/文本框)

  • java.awt.event 包 – 事件处理,后台功能的实现。

5.2JavaFx配置

具体配置参考此博客

简单来说,配置主要分为以下几步

  • 下JDK(7以上)
  • 装JavaFx Scence Builder 下载链接
  • IDEA装JavaFx插件
  • IDEA指定JavaFx Scence Builder的可执行文件的路径
  • 使用MVC模式进行开发
5.3 JavaFx项目开发
  • Main.java
    包括一个start()方法main()方法start()方法是所有JavaFX应用程序的入口,是必需的;
    而当JavaFX应用程序是通过JavaFX Packager工具打包时,main()方法就不是必需的的了,因为JavaFX Package工具会将JavaFX Launcher嵌入到JAR文件中。该文件使用FXMLLoader类,该类负责加载FXML源文件sample.fxml并返回结果对象图。
  • sample.fxml
    fxml源文件,引用样式表Controller、定义JavaFX组件等,即跟用户界面相关的定义都在这里
  • Controller.java
    定义sample.fxml中的组件id并实现sample.fxml事件引发的方法,主要用于键盘鼠标事件的处理
  • 本博主开发的话,会自定义一个Model文件夹,存放业务逻辑与数据读取方法。
六、总结

关于JDBC的一些内容会在后续的Spring系列结合Mybatis-plus进行归纳,敬请期待。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/876591.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存