JavaSE
多态
- 父类引用子类的对象
- 把子类转化为父类,向上转型
- 把父类转化为子类。向下转型:强制转换
- 方便方法的调用,减少重复的代码,简洁!
抽象类
- abstract关键字在类上,那么这个类就是抽象类
- JAVA中是单继承机制,但是接口可以实现多继承
- abstract 如果修饰方法,那么这个方法就是抽象方法
- 如果子类继承了抽象类,那么他就必须去实现父类的抽象方法
- 抽象类的所有方法,继承了的他的子类就必须去实现他们的抽象方法
- 除非子类也是抽象的,那么抽象方法就由子子类实现
抽象类的特点
- 不能new这个抽象类,只能靠子类去实现它
- 抽象类中可以写普通方法
- 抽象方法一顶要写到抽象类中
接口
接口的作用
- 接口是一个约束
- 接口可以定义一些方法,让不同的人实现
- 接口中的方法都是public abstract,都是抽象的
- 接口中的常量都是 public static final
- 接口不能直接被实例化,因为接口中没有构造方法
- 可以实现多个接口,implements可以实现多个接口
- 实现接口必须要重写接口中的方法
内部类
- 通过外部类可以来实例化一个内部类
- 内部类可以获得外部类的私有(private)属性
- 在方法里面写class类,这个类叫做局部内部类
- 一个java类中可以有多个class,但是只能有一个public class类
- 可以直接new一个匿名内部类,不需要名字,直接.方法就行了:new Demo01.a();
异常
Error和Exception的区别
- Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这种异常时,java虚拟机(JVM)一般会选择终止线程。
- Exception通常情况下是可以被程序处理的,并且在程序中应该可以竟可能的去处理这些异常。
异常处理的五个关键字
- try
- catch
- finally
- throw
- throws
五个关键字的用法
- try:里面放置待监控的代码块,监控其中的代码
- catch:捕获try中的异常
- finally:不管程序是否异常,都会执行finally里面的代码,用来处理善后工作,比如关闭资源管理等等
- throw:在方法中主动抛出一个异常,一般用在方法体内
- throws:假设在方法中,处理不了这个异常,那么就要用throws在方法上抛出,一般要和try-catch组合使用
异常实际应用中的经验总结
- 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
- 在多重catch块后面,可以加一个catch(Exception)来处理可能被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量去处理异常,切忌只是简单的调用printStackTrace()去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
- 精良添加finally语句块去释放占用的资源
多线程
重点掌握
- 线程实现
- 线程同步
多线程核心概念
- 线程就是独立的执行路径
- 在程序运行时,及时没有自己创建的线程,后台也会有多个线程,比如主线程gc线程等等
- main()称之为主线程,为系统的入口,用于执行整个程序
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的
- 对同一份资源进行操作是,会存在资源抢夺的问题,需要加入并发控制
- 线程会带来额外的开销,如cpu调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
多线程的创建方式
-
继承Thread类,重写run()方法,调用start开启线程
-
创建一个线程声明为Runnable接口,实现run()方法,创建线程的对象,调用start()方法来启动线程
-
实现callable接口,创建执行服务、提交执行、获取结果、关闭服务
public class ThreadNew { public static void main(String[] args) { //启动 new MyThread1().start(); new Thread(new MyThread2()).start(); FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3()); new Thread(futureTask).start(); try { Integer integer = futureTask.get(); System.out.println(integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } //1.继承Thread类 class MyThread1 extends Thread{ @Override public void run() { System.out.println("MyThread1"); } } //2.实现Runnable接口 class MyThread2 implements Runnable{ @Override public void run() { System.out.println("MyThread2"); } } //3.实现callable接口 class MyThread3 implements Callable{ @Override public Object call() throws Exception { System.out.println("MyThread3"); return 100; } }
注意
- 线程开启不一定立即执行,由cpu调度安排
对比 继承Thread类 和 实现Runnable接口
继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免oop单继承的局限性
实现Runnable接口
- 实现接口Runnable具备多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 推荐使用:避免单继承的局限性,灵活方便,方便同一个线程被多个线程使用
Callable的好处
- 可以定义返回值
- 可以抛出异常
- 但是实现比较复杂
静态代理模式
静态代理模式总结
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代理真实角色
静态代理模式的好处
- 代理对象可以做很多真实对象做不了的事情
- 真实对象专注做自己的事情
Lambda表达式
函数式接口的定义
任何接口,如果只包含了唯一一个抽象方法,那么这就是一个函数式接口。
Lambda表达式的使用前提
对于函数式接口,我们可以通过Lambda表达式来创建该接口的对象
like = ()->{
System.out.println("I like lambda5");
};
like.lambda();
为什么要使用Lambda表达式
- 避免匿名内部类定义过多
- 可以让你的代码看起来更简洁
- 去掉一堆没有意义的代码,只留下核心的逻辑
Lambda表达式总结
- lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行代码,那么就要用代码块包裹
- 前提是接口为函数式接口,即接口中只有一个方法
- 多个参数也可以去掉参数类型,要去掉就一起去掉,必须加上()
- 不同的参数类型也可以
- 因为Runnable接口(多线程实现方法2)中只有一个run()方法,所有很适合用lambda表达式
线程Thread类的方法
线程停止
- 建议让线程正常停止—>利用次数,不建议死循环
- 建议使用标志位flag—>设置一个标志位flag
- 不要使用stop或者destory等过时的,或者JDK不建议使用的方法
线程休眠
- sleep()指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延时,倒计时等
- 每一个对象都有一个锁,sleep不会释放锁
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转化为就绪状态
- 让CPU重新调度,礼让不一定成功,看CPU心情
线程Join
- Join合并线程,待次线程执行完成后,再执行其他的线程,其他线程阻塞
- 可以想象为插队
线程状态
线程中断或者结束,一旦进入死亡状态,就不能再次启动(线程只能启动一次)
thread.getState()
可以用上面这个方法观测线程的状态
线程优先级
优先级低只是一味这获得调度的概率第,并不是优先级低就不会被调用了,这都看CPU的心情
守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程启动完毕
- 虚拟机不必等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等等
线程死锁
产生死锁的四个必要条间
- 互斥条件:一个资源只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得资源,在未使用万之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
synchronized 和 Lock 的对比
-
Lock是显示锁(手动开启和关闭锁,别忘记关闭锁)synchronized 是隐式锁,出了作用域自动释放
-
Lock只有代码块锁,synchronized 有代码块锁和方法锁
-
使用Lock锁,JVM将花费较少的时间来调整线程,性能更好。并且具有更好的拓展性(提供更多的子类)
-
优先使用顺序:
Lock > 同步代码块(已经进入了方法体,分配了相应的资源) > 同步方法(在方法体之外)
集合
Collection常用方法
-
size():判断集合长度
-
add():添加元素
-
addAll():另一个集合添加到当前的集合中
-
clear():把集合中的元素清空
-
isEmpty():判断集合是否为空
-
remove():删除数组中的元素,可以又返回值,删除成功返回true,失败返回false
-
removeAll():删除当前集合所包含集合中的所有元素
-
contains():判断当前集合是否有当前元素
判断依据:根据当前元素所在的类中的equals()方法来进行判断
-
containsAll(Collections c):当前集合中的元素是否包含形参c中的元素
-
hashCode():获得集合的地址值
-
equals():比较级和中的所有元素是否相同
-
retainAll():求两个集合的交集,并返还给原集合
-
toArray();把集合转化为数组对象
如果存的是自定义类,要求重写equals()方法!
两种for循环的比较
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(123);
c.add("aa");
c.add(new Person("kele", 20));
c.add(new Person("kekou",19));
Object[] o = c.toArray();
//普通for循环
for (int i = 0; i < o.length; i++) {
System.out.println(o[i]);
}
//JDK5.0之后加强的for循环
for (Object o1 : o) {
System.out.println(o1);
}
}
注意输出的值是循环内定义的值
格式: for(类型 参数名 : 数组或集合类){ }
类型必需与数组或集合类的类型相同,
参数的值是动态的,所有数组或集合类的每一个值。
List接口的特点
- 存储和读取的顺序一致
- 有索引
- 有序的,元素可以重复
特有的方法
- void add(int index, Object o):添加
- object get(int index):获取
- Object remove(int index):删除,返回值是删除的元素
- Object set(int index, Object o):更新设置,返回值是该位置之前的元素
- indexOf(Object o):返回o在集合中第一次出现的下标,没有就返回-1
- lastIndexOf((Object o):返回o在集合中最后一次出现的下标,没有就返回-1
- subList(int fromIndex, int toIndex):截取一个集合,含头不含尾
LinkedList特有的方法
- addFirst():把元素添加到链表的第一个
- getFirst():获取链表的第一个元素
- removeFirst():删除链表的第一个元素,有返回值,返回值为这个删除的元素
- getList()
- removeList()
getFirst()方法和栈相似,每次把元素添加到链表的最前面,所以最先添加的元素在最后面
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.addFirst("aa");
list.addFirst("bb");
list.addFirst("cc");
list.addFirst("dd");
list.addLast("ee");
System.out.println(list.getFirst());
System.out.println(list.getLast());
System.out.println("----------");
for (Object o : list) {
System.out.println(o);
}
}
运行结果:
HashSet的特点
- 无序
- 唯一,元素不可重复
在HashSet实例化对象后添加对象时,首先调用对象所在类的hashCode()方法,计算此对象的哈希值。这个哈希值就决定了这个对象的Set集合中的位置,如果这个位置没有对象进行存储,则这个对象就存储到这个位置。如果这个位置有对象了,在通过equals方法来进行比较,如果equals方法比较结果一样,就不在进行存储了。如果equals方法不一样,就在下一个位置存储。
一般情况下,我们会保证hashcode和equals一致
重写hashcode方法和equals方法上。
LinkedHashSet
LinkedHashSet:使用链表来维护一个集合
我们遍历元素的时候是按照元素添加的顺序进行的
LinkedHashSet插入的性能略低于HashSet,但是迭代的时候性能会好一些。
TreeSet
- 默认会把TreeSet里的元素排序,存储的比如String或者包装类,他都按自然排序
- 存储的对象应该属于同一类
- 存储对象是自定义对象时
- 让对象自身具有可比性,对象所在的类需要实现Comparable接口,如果不实现,会出现classCastExcpetion异常
- 容器本身制定比较规则
实现Comparable中的compareTo()方法
@Override
public int compareTo(Object o) {
return 1;
}
//比较器
@Override
public int compareTo(Object o1, Object o2) {
Person p1 = (Person)o1;
Person p2 = (Person)o2;
int i = p1.getAge().compaterTo(p2.getAge());
if(i == 0){
return p1.getName().compareTo(p2.getName());
}
return i;
}
注意:
- 返回值为0,代表所有元素都一样,只会存储第一个数据
- 返回值为正整数,按顺序存取
- 返回值为负整数,逆序存取
工具类
用的时候查API,不需要死记
collections
- max():最大值
- min():最小值
- reverse():集合逆序
- sort():排序
Arrays
- binarySearch():使用二分查找搜索指定的数组
- asList():返回一个受指定的数组支持的固定大小的列表
小结
Collection: add(Object obj) addAll(Collection c) size() clrear() isEmpty()
remove(Object obj) removeAll(Collection c) retainAll(Collection c)
contains(Object obj) containsAll(Collection c) hashCode()
iterator(), toArray()
-->List:有序,可以重复存储元素
特有的: add(int index, Object obj) remove(int index)
Object set(int index, Object obj)
Object get(int index) int indexOf(Object obj)
int lastIndexOf(Object obj) List subList(int from, int to)
-->ArrayList:内部是一个数组结构,不同步,代替了Vector,查询快
-->LinkedList:内部数据结构是一个链表, 不同步,增删快
-->Vector:内部数据结构也是数组,同步的。增删查都慢
-->Set:无序,元素唯一
Set接口与Collection接口中的方法一致
-->HashSet:内部数据结构是哈希表,不同步。
怎么保证存储的元素唯一呢?
通过对象的hashCode和equals方法来确定的
如果对象的hashCode不同,就不用再判断equals方法了,直接存储到集合中
如果对象的hashCode相同,就需要根据equals方法来进行判断
如果结果为true,视为相同,不再进行存储
如果为false,视为不同,就进行存储(不建议)
注意:如果元素存储到hashSet中,必须要覆盖hashCode和equals方法
尽量这两个方法保持一致
-->LinkedHashSet:是其子类,当我们遍历集合的时候,是按照我们添加顺序进行的
频繁的遍历,但是增删比较少,可以选择这种集合
-->TreeSet:可以对元素进行排序,是不同的
判断元素唯一性的方式:就是根据比较方法返回的结构,0(相同), 不存储
排序的方式?
1.元素自身具有比较的功能,实现Comparable接口,重写compareTo方法
2.元素自身不具有比较功能,没有实现Comparable接口,这个时候怎么办呢?
定义一个类,实现Comparator接口,重写compare方法
把这个类创建的对象作为参数,通过构造函数传递给TreSet集合
Map: 一次添加一对元素, Collectio一次添加一次元素
Map也成为双列集合, Collection也成为单列集合
Map中key必须唯一
put(Object key, Object value) remove(Object key) clear(), size()
isEmpty() Object get(Object key) boolean containsKey(Object key)
boolean containsValue(Object value)
循环方式?
1. keySet -->得到key的集合,循环得到每一个key,通过get(key),获取value
2.entrySet -->得到key和value整体的一个Map.Entry集合,循环得到每一个Map.Entry对象
通过调用getKey()和getValue(),获取key和value
-->HashMap:内部数据结构是哈希表,不同步。允许null作为key或者value
-->LinkedHashMap:是其子类,可以按照添加进map的顺序进行遍历
-->TreeMap:内部数据结构是二叉树,是不同步的。可以对key进行排序
-->HashTable:很少用
-->Properties:根据IO来一起使用
泛型
泛型:jdk1.5版本之后出现的一种安全机制
泛型擦除技术:是给编译器使用,用于编译期,确保了类型的安全运行,运行期间,.class文件中是没有泛型的。
为什么要有泛型擦除技术?为了兼容运行的类加载器。
泛型补偿:在获取集合元素时,不用再做类型强转了。这既是泛型补偿
Collection<? extends Person>只能存储Person或者Person的子类。
Collection<? supper Person>只能存储Person或者Person的父类。
泛型的好处
- 将运行期的类型转换异常转移到了编译器
- 避免了强制转换的麻烦
泛型的用处
- 参数化类型,将具体的类型参数化
自定义泛型
- 泛型可以用在类上
- 泛型还可以用在方法上
- 还以用在返回值上
- 当方法是静态的,不能访问类上定义的泛型,如果静态方法要使用这个泛型,那么就只能把泛型定义在方法上。
IO流
File类
- File(String pathname):可以把一个存在或者不存在的文件或者文件目录封装成一个对象
- File(String parent, String child):
- File(File parent, String child):
File类的方法
- 创建删除
- createNewFile():指定的文件不存在,创建文件并返回true,否则返回false
- mkdir():创建文件夹
- mkdirs():创建多级的文件夹
- delete():删除单个文件或者文件夹,如果文件夹里有文件,无法删除返回false
- 判断
- exists():判断文件或者文件夹是否存在
- isDirectory():判断文件是不是一个文件夹
- isFile():判断文件对象是不是一个文件
- isAbsolute():判断当前路径是不是绝对路径
- isHidden():判断文件是否隐藏
- 获取
- getAbsolutePath():获得文件的绝对路径:返回路径字符串
- getParentFile():获取当前路径的父路径:以文件对象的形式返回
- getParent():获取当前路径的父路径,以字符串形式返回
- getPath():获取当前路径
- getName():获取当前文件或者文件夹名称
- lastModfied():获取文件最后修改时间
- length():获取文件长度
- getTotalSpace():获取文件占用分区总大小
- rename():给文件重命名
- 文件目录
- list():以字符串形式返回当前路径下的所有文件和文件夹
- listFiles():以File对象形式返回当前路径下的所有文件和文件夹
File类的递归
打印文件夹和文件名称
public static void showList(File file) {
if (file.isDirectory()){
File[] files = file.listFiles();
for (File file1 : files) {
if(file1.isFile()){
System.out.println("文件:" + file1.getName());
}else if(file1.isDirectory()){
showList(file1);
}
}
}
System.out.println("文件夹:" + file.getName());
}
删除文件夹内的所有文件和文件夹
public static void remove(File file){
if (file.isDirectory()){
File[] files = file.listFiles();
for (File f : files) {
if(f.isFile()){
f.delete();
}else if(f.isDirectory()){
remove(f);
}
}
}
file.delete();
}
字符流
概述
单个的读取字符,只能读文本数据
装饰者设计模式
- 对原有类进行了功能增强
- 他与继承有什么不同
- 继承:
- 被增强的对象固定
- 被增强的内容也是固定的
- 装饰者模式
- 被增强的对象是可以切换的
- 被增强的内容也是固定的
- 动态代理模式
- 被增强的对象可以切换
- 被增强的内容也可以切换
- 继承: