目录
集合
可以动态保存任意多个对象。提供了一系列方便操作对象的方法:add、remove、set、get等。使用集合添加,删除元素更简洁。
集合框架体系(重要)
集合主要是两组:单列集合和双列集合。
单列集合
双列集合
Collection接口实现类的特点
Collection接口和常用方法
实现子类ArrayList
Collection接口遍历元素方式
使用Iterator(迭代器) 方式
1.Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。
2.所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
3.Iterator的结构:
Iterator iterator = 集合.iterator();// 先得到集合对应的迭代器
while(iterator.hasNext()){ // 使用while遍历
Object obj = iterator.next();
System.out.println(obj);
}
//当退出while循环后,这时iterator迭代器指向最后的元素。
//如果希望再次遍历,需要重置迭代器。iterator = 集合.iterator();
4.Iterator仅用于遍历集合,Iterator本身并不存放对象。
for循环增强方式
增强for循环,可以代替iterator迭代器,特点:增强for就是简化版的iterator,(底层仍然是迭代器)本质一样。只能用于遍历集合或数组。(快捷键:I)
基本语法
for(元素类型 元素名:集合名或数组名){
访问元素
}
List接口
List接口是Collection接口的子接口。
1.List集合类中元素有序(即添加顺序和取出顺序一致),并且可以重复。
2.List集合中的每个元素都有其对应的顺序索引,即支持索引。(索引从0开始)
3.List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号取容器中的元素。
4.JDK API中List接口的实现类有:Vector、ArrayList、LinkedList等。
5.
List的三种遍历方式
ArrayList注意事项
ArrayList底层结构和源码分析(重要)
Vector底层结构和源码剖析
ArrayList和Vector的比较
LinkedList底层结构
ArrayList和LinkedList比较
Set接口以及常用方法
1.无序(添加和取出的顺序不一致),没有索引。取出的顺序虽然不是添加的顺序,但是取出的顺序固定。
2.不允许重复元素,所以最多包含一个null。
3.JDK API中Set接口的实现类有:HashSet、TreeSet等
Set接口的常用方法
和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样。
Set接口的遍历方式
同Collection的遍历方式一样,因为Set接口是Collection接口的子接口。
1.可以使用迭代器。
2.增强for。
3.不能使用索引的方式来获取。
HashSet
1.HashSet实现了Set接口。
2.HashSet实际上是HashMap。源码:public HashSet(){ map = new HashMap<>();}
3.可以存放null值,但是只能有一个null。
4.HashSet不保证元素是有序的,取决于hash后,再确定索引的结果。(不能保证存放元素的顺序和取出顺序一致)
5.不能有重复的元素/对象。在前面Set接口使用已经讲过。
HashSet底层机制
1.HashSet底层是HashMap,HashMap底层是(数组 +链表+红黑树) 。
2.HashSet的添加元素底层实现(hash() + equals())。
(1)添加一个元素时,先得到hash值---会转成->索引值。(2)找到存储数据表table,看这个索引位置是否已经存放的有元素。(3)如果没有直接加入。如果有调用equals比较,如果相同,就放弃添加,如果不相同,则添加在最后。(4)在java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小 >= MIN_TREEFY_CAPACITY(默认64),就会进化树化(红黑树)。
HashSet扩容和转换成红黑树机制
1.HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor)是0.75 = 12.
2.如果table数组使用了临界值 12,就会扩容到16 * 2 = 32,新的临界值就是32*0.75 = 24,以此类推。
3.在Java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树),否则仍然采用数组扩容机制。
LinkedHashSet
1.LinkedHashSet是HashSet的子类。
2. LinkedHashSet底层是一个LinkedHashMap,底层维护了一个 数组+双向链表。
3.LinkedHashSet 根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的。
4.LinkedHashSet 不允许添加重复元素。
LinkedHashSet底层机制
1.在LinkedHashSet中维护了一个hash表和双向链表(LinkedHashSet有head和tail)。
2.在每一个结点有before和after属性,这样可以形成双向链表。
3.在添加一个元素时,先求hash值,再求索引,确定该元素在table中的位置,然后将添加的元素加入到双向链表(如果已经存在,不添加【原则和hashset一样】)。
示意代码:
tail.next = newElement
newElement.pre = tail
tail = newElement;
4.这样的话,我们遍历LinkedHashSet也能确保插入顺序和遍历顺序一样。
Map接口
Map接口实现类的特点(JDK8)
Map接口常用方法
Map接口遍历方法
HashMap
HashMap底层机制及源码剖析
1.(k,v)是一个Node实现了Map.Entry<K,V>,查看HashMap的源码可以看到。
2.jdk7.0的hashmap底层实现[数组+链表],kdk8.0底层[数组+链表+红黑树]。
HashTable
1.存放的元素是键值对:即K-V。
2.hashtable的键和值都不能为null。否则会抛出NullPointerException。
3.hashTable 是线程安全的,hashMap是线程不安全的。
4.hashTable使用方法基本上和hashMap一样。
HashMap和hashTable对比
Properties
总结------开发中如何选择集合实现类
Collection工具类
Collections是一个操作Set、List和Map等集合的工具类。
Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作。
操作排序
泛型
泛(广泛)型(类型)--> Integer、String、Dog等。
1.泛型又称参数化类型,是jdk5.0出现的新特性,解决数据类型的安全性问题。
2.在类声明或实例化时只要指定好需要的具体类型即可。
3Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。
4.泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
泛型的好处
1.编译时,检查添加的元素的类型,提高了安全性。
2.减少了类型转换的次数,提高效率。
3.不再提示编译警告。
泛型的语法
泛型的说明
interface 接口<T>{} 和 class 类<K,V>{}
比如:List、ArrayList
说明 :
1.其中T,K,V不代表值,而是表示类型。
2.任意字母都可以。常用T表示,是Type的缩写。
泛型的实例化
要在类名后面指定类型参数的值(类型)如:
1.List<String> strList = new ArrayList<String>();
2.Iterator<Customer> iterator = customers.iterator();
泛型使用细节
1.泛型类型只能是引用类型,不能是基本数据类型。
2.在给泛型制定具体类型后,可以传入该类型或者该类型的子类型。
3.泛型使用形式:
List<Integer> list1 = new ArrayList<Integer>();
ArrayList<Integer> list2 = new ArrayList<Integer>(); // 用List来接受
推荐写法:(实际开发中往往简写,编译器会进行类型推断)
ArrayList<Integer> list2 = new ArrayList<>();
4.List list3 = new ArratList(); // 默认给的泛型是[<E> E就是Object]
自定义泛型类
基本语法:class 类名<T,R....>{ 成员 } // T,R泛型标识符,一般为大写单个字母
自定义泛型类注意细节
1.普通成员可以使用泛型(属性,方法)。
2.使用泛型的数组,不能初始化。
3.静态方法中不能使用类的泛型。
4.泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)。
5.如果在创建对象时,没有指定类型,默认为Object。
自定义泛型接口
基本语法:interface 接口名<T,R...>{ }
自定义泛型接口注意细节
1.接口中,静态成员也不能使用泛型。
2.泛型接口的类型,在继承接口或实现接口时确定。
3.没有指定类型,默认为Object。
自定义泛型方法
基本语法:修饰符 <T,R...>返回类型 方法名(参数列表){ }
自定义方法注意细节
1.泛型方法,可以定义在普通类中,也可以定义在泛型类中。
2.当泛型方法被调用时,类型会被编译器确定。
3.Public void eat(E e){} 修饰符后没有<T,R...> eat方法不是泛型方法,而是使用了泛型。
泛型的继承和通配符
1.泛型不具备继承性。
List<Object> list = new ArrayList<String>(); //错误。泛型布具备继承性。
2.<?>:支持任意类型泛型。
3.<? extends A>:支持A类以及A类的子类,规定了泛型的上限。
4.<? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限。
Java绘图
坐标系
坐标原点位于左上角,以像素为单位。在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。
像素
计算机在屏幕上显示的内容都是由屏幕上的每一个像素组成的。例如:分辨率为800*600的显示器,表示计算机屏幕上的每一行由800个点组成,共有600个行,整个屏幕共有480 000个像素。像素是一个密度单位,而厘米是长度单位,两者无法比较。
Graphics类
实例:在IDEA中画形状,并在控制面板中显示。(见文档Draw.Drawtest)
控制小球受到键盘的控制,上下左右运动。(见文档Draw.MoveBall)
Java事件处理机制
Java事件处理是采取”委派时间模型“。但事件发生时,产生事件的对象,会把此”信息“传递给”事件的监听者"处理。这里所说的“信息”实际上就是java.awt.event事件类库里某个类所创建的对象,把它称为“事件的对象”。
线程
线程相关概念
程序
程序是为完成特定任务、用某种语言编写的一组指令的集合。程序简单说就是运行代码。
进程
进程是指运行中的程序。例如我们使用微信,就启动了一个进程,操作系统就会为该进程分配内存空间。
进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡过程。
线程
1.线程是由进程创建的,是进程的一个实体。
2.一个进程可以拥有多个线程。
单线程和多线程
单线程:同一个时刻,只允许执行一个线程。
多线程:同一个时刻,可以执行多个线程,比如:一个QQ进程可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件。
并发和并行
并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉。简单地说,单核cpu实现的多任务就是并发。
并行:同一个时刻,多个任务同时执行。多核cpu可以实现并行。
Runtime runtime = Runtime.getRuntime(); // 获取当前pc的cpu数量 int cupNums = runtime.availableProcessors(); System.out.println("当前cpu个数为: "+cupNums);
创建线程
创建线程的两种方式:
1.继承Thread类,重写run方法。
2.实现Runnable接口,重写run方法。
两种创建方法的区别:
1.从java的设计来看,通过继承Thread或者实现Tunnable接口来创建线程本质上没有区别。从jdk帮助文档可以看到Thread类本身就实现了Runnable接口。
2.实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制。
线程注意细节:
1.start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程。
2.线程优先级的范围,从低到高为:MIN_PRIORITY 1 / NORM_PRIORITY 5 / MAX_PRIORITY 10。
3.interrupt,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠进程。
4.sleep:线程的静态方法,使当前线程休眠。
线程常用方法
用户线程和守护线程
1.用户线程:也叫工作线程,当线程的任务执行完或通知方式结束。
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。setDaemon(true);
3.常见的守护线程:垃圾回收机制。
线程7大状态
之所以说7种状态,是因为RUNNNALBE中又分为Ready和Running两种状态。
线程生命周期
线程同步机制
1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
2.也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。
线程同步方法
Synchronized:
(1)同步代码块:synchronized (对象){// 得到对象的锁,才能操作同步代码
// 需要被同步代码;}
(2)synchronized还可以放在方法声明中,表示整个方法为同步方法。
public synchronized void m(String name){
// 需要被同步的代码;}
互斥锁
静态方法中实现一个同步代码块就要写:synchroized(类名.class){}
互斥锁的注意事项
1.同步方法如果没有static修饰:默认锁为this。
2.如果方法使用static修饰,默认锁对象:当前类.class。
3.实现的落地步骤:
需要先分析上锁的代码。
选择同步代码块或同步方法。
要求多个线程的锁对象为同一个即可。
线程的死锁
多个线程占用了对方的锁资源,但不肯相让,导致了死锁。
释放锁
以下操作会释放锁:
1.当前线程的同步方法、同步代码块执行结束。
2.当前线程在同步代码块、同步方法中遇到break、return。
3.当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
4.当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
以下情况不会释放锁:
1.线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁。
2.线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。
IO流
文件
文件,简单讲就是保存数据的地方。
文件流
文件在程序中是以流的形式来操作的。
流:数据在数据源(文件)和程序 (内存)之间经历的路径。
输入流:数据从数据源(文件)到程序(内存)之间的路径。
输出流:数据从程序(内存)到数据源(文件)的路径。
创建文件的方法
// 三种创建文件的方式
// 第一种方式
@Test
public void creat01(){
String filePath = "d:\\test1.txt";
File file = new File(filePath);
try {
file.createNewFile();
System.out.println("文件创建成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
// 第二种方式
public void creat02(){
File parentFile = new File("d:\\");
String fileName = "test2.txt";
File file = new File(parentFile, fileName);
try {
file.createNewFile(); // 这里才会在磁盘中创建该文件
System.out.println("文件创建成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
// 第三种方式
@Test
public void creat03(){
String parentPath = "d:\\";
String filePath = "test3.txt";
File file = new File(parentPath, filePath);
try {
file.createNewFile();
System.out.println("文件创建成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
获取文件相关信息
分别为:获得文件名字。文件绝对路径。文件父级目录。文件大小(字节)。文件是否存在。是不是一个文件。isDirectory:是不是一个目录。
目录操作和删除文件
mkdir:创建一级目录。
mkdirs:创建多级目录。
delect:删除空目录或文件。
IO流原理及流的分类
1.I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等。
2.Java程序中,对于数据的输入/输出操作以“流(stream)”,的方式进行。
3.java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据。
流的分类
Java的IO流共涉及40多个类,实际上非常规则,都是由以上四个抽象基类派生出来的。
由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
InputStream字节输入流
FileReader和FileWriter
每次读取或者写入文件之后都要close或者flush文件。
节点流处理流
节点流和处理流的区别和联系
处理流的功能主要体现在以下两方面
对象流
序列化和反序列化
1.序列化后保存的文件格式,是按照他自己的格式来保存。(.dat文件)
2.基本数据类型int,boolean,char,String他们的包装类等默认实现了Serializable接口,自己定义的类要手动实现Serializable接口。
如果我们希望在反序列化的模块中调用自己定义类的方法,需要向下转型。
需要我们将自己定义的类,放到可以引用的位置。
对象流的细节
标准输入输出流
转换流
打印流
只有输出流,没有输入流。
PrintStream out = System.out; // 字节打印流
字符打印流
Properties类
Properties类是专门用于读写配置文件的集合类。
配置文件的格式:
键=值 // 必须按照这种格式来,注意:键值对之间不需要有空格,值不需要引号引起来,默认类型是String。