目前论文稍微有了一些眉目,在这个blog里记录自己每天所做的事,然后也记录一些关键的知识点。
3.4
-
可变长参数 使用…形式
-
静态方法和非静态方法的调用区别?
调用静态的时候直接Class.Method即可,非静态要new
静态方法是和类一起加载的,非静态方法是实例化后才存在的
初始化时依次执行静态代码块、匿名代码块、构造方法。但是其中静态代码块只执行一次。 -
值传递与引用传递
使用方法对属性是无法对值进行改变的,但是在引用传递中,具体对象的属性是可以被修改的 -
面向对象编程的本质:以类的形式组织代码,以对象的组织封装数据
-
类在实例化后会返回自己的对象,对象是类的一个具体实例
-
面对对象的基本知识点小结
1.类与对象:
类是模板,对象是具体实例
2.方法:
定义,调用(?)
3.对应的引用
引用类型:基本类型
对象是通过引用来调用的
4.属性
默认初始化
5.对象的创建和使用
6.类:
静态的属性 属性
动态的行为 方法 -
修饰符限定范围从大到小:public protected 默认 private
-
重写是方法的重写,和属性无关
-
静态方法和非静态方法有区别
-
静态方法调用只与左边有关(左边是编译类型,右边是运行类型)
-
非静态方法才会有重写
3.5
不建议听狂神的课,他的那个我听着太混乱了,回归韩顺平。学习方式暂定以看笔记为主,笔记不懂或者设计底层逻辑讲解时看视频(一切为了效率)
静态变量
1.静态变量是什么(公有变量)
静态变量是该类下所有对象共享的一个变量,该类的每一个对象都可以访问,且访问获得的值是相同的。
2.语法:
修饰+static+数据类型+name;
3. 访问方式:
类名.方法名
4.注意事项
- 一个变量需要类中所有对象使用时可以考虑使用静态变量
- 与实例变量(普通属性(实例变量/普通变量/非静态变量))的区别:共享与独享
- 访问方式的区别:可以类名.变量名/不可以
- 在加载类的时候进行了初始化(因此可以在不初始化对象的情况下使用类名使用)
- 生命周期:随类而行
类方法
语法,访问方式
与类变量基本一致
使用场景
当方法中不涉及和对象相关的成员,则可以将方法设置为静态方法(提高开发效率)
例子:Math类,Arrays类,Collections集合(这些还没学到,后面注意讲没讲)
//开发自己的工具类时,可以将方法做成静态的,方便调用
class MyTools { //求出两个数的和
public static double calSum(double n1, double n2) {
return n1 + n2;
}
//可以写出很多这样的工具方法...
}
注意事项
- 结构信息存储在方法区中,无this函数(对应普通方法中隐含this)
- 调用方式与普通方法有区别:可以类名.方法名(普通方法不可以)
- 类方法中不允许存在this或super(this和super都是和对象有关的关键词)
- 类方法只能访问类方法和类变量
main方法
理解main方法
public static void main(String[] args){}
- java虚拟机需要调用main方法,因此范围应为public
- 在初始化main方法是不需要创建对象,因此应为static
- 接收String可以传参(在执行程序时后面传入)
main注意事项
和静态方法一样,不能直接访问非静态成员,需实例化后访问
IDEA传参
代码块
简单理解为只有方法体的方法,在加载类/创建对象时隐式调用。
语法
[static]/*对应非静态代码块[静态代码块]*/{
code
};
理解
- 相当于另一种形式的构造器,可以进行初始化操作
- 用于提取多个构造器中的重复部分
- 代码块调用优先于构造器
注意事项
- 静态代码块:作用是对类进行初始化,随着类的加载执行一次。
普通代码块:创建一个对象则被隐式调用一次。 - 那么类什么时候会被加载呢?
1.创建对象
2.创建子类对象时,其父类也会被加载(加载时先加载父类,后加载子类)
3.使用类的静态成员时 - 在创建对象时,类中的调用顺序:静态代码块,静态属性>非静态代码块,非静态属性>构造器
- 在有父类和子类的继承关系时如下:
父类静态(代码块,变量)>子类静态(代码块,变量)>父类普通(代码块,变量)>父类构造器>子类普通(代码块,变量)>子类构造器 - 静调静,普通调所有
设计模式
在设计模式中会使用到静态类/方法
- 采用设计模式的初衷:和棋谱类似
- 主要介绍两种单例设计模式:饱汉式、饿汉式
饱汉式
- 步骤如下:
1.将构造器私有化(使得确保只能生成一个实例)
2.在类的内部直接创建对象(对象是static类型的)
3.为了保证能够使用到这一对象,提供一个静态方法返回对象 - 起名原因:用不用得到这个对象,该对象都会生成(可以想想为什么)
class girlFriend {
private int age;
private static girlFriend gf = new girlFriend(18);
private girlFriend(int age) {
this.age = age;
}
public static girlFriend getInstance() {
return gf;
}
}
饿汉式
- 步骤如下:
1.构造器私有化
2.定义一个static对象
3.提供public方法返回girlF对象
4.懒汉式与饿汉式的区别:在getInstance中来生成对象
class girlF{
private String name;
public static girlF girl;
private girlF(String s){
this.name = s;
}
public static girlF getInstance(){
if (girl == null){
girl = new girlF("qx");
}
return girl;
}
}
二者的区别
- 最主要的区别在于创建对象的时机不同
- 饿汉式不存在线程安全问题,懒汉式存在(这个存疑,后面学到线程回顾)
- 是否存在资源浪费的可能
final关键字
不希望类被继承/父类被子类覆盖或重写/属性的值被改变/局部变量被改变时,可以使用final进行修饰
final细节
- 一般用final修饰的属性被称为常量
- 被final修饰的属性必须赋初值,且不能更改,赋值位置有:定义、构造器、代码块
- 如果final修饰的属性是静态的,则初始化位置不能在构造器中(类加载在构造器初始化之前)
- final类不能被继承,但是可以实例化对象
- 如果非final类有final方法,该方法不可被重写,可被继承
- final不能修饰构造器
- 包装类都是final类(Integer,Double,Float,Boolean,String)
- final 和 static结合使用,可以直接调用属性,不用初始化类
但是如果static final方法的话则类还是会被初始化(回顾顺序)
public class pr01 {
public static void main(String[] args) {
System.out.println(A.age);
}
}
class A{
public static final int age = 50;
static {
System.out.println("new");
}
}
//输出为50,未输出new
抽象类
为什么会出现?因为只是设定一个框架,提前实现没有意义(都需要去重写)
抽象类与抽象方法的语法
访问修饰符+abstract+类名{}
访问修饰符+abstract+返回类型+方法名(){}
使用细节
- 抽象类不能被实例化
- 抽象类可以没有抽象方法
- 有抽象方法的一定是抽象类
- abstract只能修饰类与方法
- 抽象类可以有任意成员(非抽象方法,构造器,静态属性等)
- 抽象方法不能有主体
abstract void aaa()/*{}*/ ;
- 继承抽象类的非抽象类必须重写抽象类的所有方法
- 抽象方法并不能用private、final、static来修饰(违背了重写的要求)
今天大致就学到这里,毛学习时长大概是十二个小时,具体玩和休息了多久没有细算,但是感觉已经很累了,应该也就学了6,7个小时吧。接下来的进度还是希望一天一章,争取在3.20之前学完javase 在此之后学习web并刷题。但是项目这个要怎么解决仍是一大难关。希望能赶得上五月份的实习吧(剩啥公司就去哪了)
希望明天有效时长能达到8小时。
3.6
开始做了腾讯实习的测评,上午只学了四十分钟
抽象模板模式
public abstract class serviceJob {//抽象类 模板设计模式
public abstract void job();
public long calculateTime(){
long s = System.currentTimeMillis();
job();//动态绑定
long e = System.currentTimeMillis();
return e - s;
}
}
接口
接口与抽象类最大的区别:接口中的所有方法都必须是抽象的,但是抽象类中可以有具体的方法
基本语法
public/*修饰限定符*/ interface UserService/*接口名*/ {
//定义的属性都是常量 public static final
public static final int age = 25;
//接口中的方法都是抽象方法,public abstract都被省略了。
void add(String a);
void delete(String a);
//相当于public abstract void delete(String a);
}
public class UserServiceImpl implements UserService{
//可以有自己的属性和方法
@Override
public void add(String a) {
}
@Override
public void delete(String a) {
}
//必须实现接口的抽象方法
}
注意事项
-
接口不能被实例化
-
接口中所有方法是public方法,接口中的抽象方法可以不用abstract
下图便于理解:
-
普通类实现接口必须实现接口的所有方法(alt+enter)
-
抽象类可以不实现接口的方法
-
多继承(一个类可以实现多个接口,用“,"链接)
-
接口中的属性都是常量,访问方式为接口名.属性(对于实现接口的类也可以使用类名.属性)
-
接口不能继承类,但是可以继承其他接口
-
接口的修饰符只能是public和默认
接口与继承的不同:
- 解决的问题不同
继承:解决代码的复用性和可维护性
接口:设计,设计好各种规范方法,让其它类实现,更加灵活 - 接口比继承更加灵活
- 接口在一定程度上实现代码解耦(?)
接口的多态性质
- 参数可以是多态的 和向下转型形式一致
接口类型的变量可以指向实现了该接口的类的对象 - 一样可以实现多态数组,其中也有动态绑定
- 多态传递(?)
一个接口1实现了另一个接口2,那么2的对象实例也可以指向类A(实现了接口1)的对象,因为类实现1就需要实现2。
内部类
类的五大成员:属性,方法,代码块,构造器,内部类
最大特点:可以直接访问私有属性
四种内部类
-
定义类在局部位置(方法中/代码块):局部内部类、匿名内部类
-
定义在成员位置:成员内部类、静态内部类
-
主要是关于静态内部类,其他的遇到再研究
class Outer001{
private int n1 = 100;
public void method(){
/**
* 基于接口的匿名类
* 1.需求:使用接口A,并创建对象
* 2.传统方法:写一个类,实现接口,并创建对象
* 3.问题:如果写的这个类只用一次,很罗嗦
* 4.出现:用匿名类简化开发
* 5.cat的编译类型?A
* 6.cat的运行类型?这个匿名内部类(底层会分配)(回顾getClass)
* 7.jdk底层在创建匿名内部类后立即生成了其实例,并把地址返回给cat
* 8.匿名内部类使用一次后就不再可使用
*/
A cat = new A(){
public void cry(){
System.out.println("cat cry");
}
};
Father father = new Father("xf"){//参数会传递给Father类的构造器
public void test{
System.out.println("new test");
}
};
//也可以创建抽象匿名类
}
}
interface A {
void cry();
}
class Father{
public Father(String name){
}
public void test(){
}
}
//class cat implements A{
//
// @Override
// public void cry() {
// System.out.println("cat cry");
// }
//}
3.13
草 我之前学的都没保存吗?
枚举类、常用类(String、StringBuffer、StringBuilder)
丢失部分大致总结
- 八个包装类,实现了Comparabel接口,可以比较
- 常用方法
- String创建对象的形式,内存上的区别
- StringBuffer、StringBuilder、String区别,优缺点,常用方法
- System类
- BigInteger和BigDecimal
- 第三代日期类
草 心态崩了
开始学集合吧
集合
常见的集合类型如图所示
先说说List、Set、Queue、Map的区别
- 1是主要应对有序数据的处理,可重复
- 2中更注重对不重复的数据的处理
- 3按照特定的排队规则处理数据,数据是有序的,可以重复的
- 4使用key-value对来对数据进行存储,其中key值不可以重复,value可以重复
Collection类常用方法
遍历元素的方式
1.迭代器
public class pr01 {
public static void main(String[] args) {
@SuppressWarnings({"all"})
Collection col= new ArrayList();
col.add(new Book("四国演义","罗贯中",10.1));
col.add(new Book("小李飞刀","古龙",5.1));
col.add(new Book("红楼梦" , "曹雪芹",34.6));
//System.out.println(col);
Iterator iterator = col.iterator();
// while (iterator.hasNext()){
// Object next = iterator.next();
// System.out.println(next);
// }
//itit,ctrl + j
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//退出while循环后,iterator指向最后的元素,再执行next会报错
//希望再次遍历需要重置迭代器
iterator = col.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}
}
2.增强for循环
public class pr02 {
public static void main(String[] args) {
@SuppressWarnings({"all"})
Collection col= new ArrayList();
col.add(new Book("四国演义","罗贯中",10.1));
col.add(new Book("小李飞刀","古龙",5.1));
col.add(new Book("红楼梦" , "曹雪芹",34.6));
for (Object book : col){
System.out.println(book);
}
}
}
底层仍是迭代器
List方法
public class pr01 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("1");
list.add("0");
list.add("2");
list.add("0");
list.add("3");
// System.out.println(list.get(2));
// list.add(2,"wtf");
// System.out.println(list.get(2));
// List list2 = new ArrayList();
// list2.add("!");
// list2.add("@");
// list.addAll(2,list2);
// System.out.println(list);
// System.out.println(list.indexOf("0"));
// System.out.println(list.lastIndexOf("0"));
// Object s = list.remove(0);
// System.out.println(s);
// System.out.println(list);
list.set(2,5);
System.out.println(list);
// 注意返回的子集合 fromIndex <=subList < toIndex
System.out.println(list.subList(1,4));
}
}
对ArrayList进行源码解读
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
}
List : 表明它是一个列表,支持添加、删除、查找等操作,并且可以通过下标进行访问。RandomAccess :这是一个标志接口,表明实现这个接口的 List 集合是支持 快速随机访问 的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
Cloneable :表明它具有拷贝能力,可以进行深拷贝或浅拷贝操作。
Serializable : 表明它可以进行序列化操作,也就是可以将对象转换为字节流进行持久化存储或网络传输,非常方便。
- trimToSize 将数组的大小修改为当前大小
- ensureCapacity 在扩容前使用,确定是否要扩容
- size
- isEmpty
- cotains
- indexOf、lastIndexOf
- clone 浅拷贝(浅拷贝只是又生成了一个对象指向已有数据)
- toArray
- set 改
- get查
- add增
- remove删
- clear
- removeRange 删除一段范围内的元素
- retainAll 只保留特定元素
扩容流程
这个图就挺好的,和我自己读源码的理解差不多
Vector
Vector是线程安全的
和ArrayList区别:
LinkedList
add方法中调用lastlist尾插法来进行加入,先将最后一个元素复制last,然后新生成一个节点,规定其pre为last,其尾部为null。在此之后判断原尾结点是否为空,若为空说明原来是个空list,first指向新节点,否则将原尾结点的next与新节点连接。
remove的流程:最终调用到unlinkfirst。先生成一个节点next,指向被删除的下一个节点,将item和其next指向null,将头结点指向next节点。如果此时next为null,说明链表为空,则last也指向null,否则的话将next的pre指向null。
linkedlist头插和尾差都是O(1),指定位置插入为O(n)。
不能随机访问,未实现RandomAccess接口,因为链表形式内存地址不连续。
Set
Set接口没有get方法
特点
- 无序
- 不允许有重复元素
- 实现了以下接口:
HashSet
- HashSet实现了Set接口
- 实际上是HashMap
- 可以存放null,但是只能有一个null
- 不保证元素是有序的
- 元素是不可重复的
添加元素方式
- 初始化hashmap
- 执行add(调用map.put)
- 执行map.put(生成hash值后调用putVal)
- 执行putVal(核心算法)
1.其中涉及到扩容的部分,其中的tab == table是指在内存中的一个表table 已经初始化这个表后tab不为null
2.对情况分为四种进行讨论
1)当前索引位置为空:直接在当前table位置生成节点
2)当前索引位置不为空(234都是此种情况):如果key与当前索引p所对应的key值相等的话,则添加失败(set不允许重复)
3)判断p是不是红黑树,如果是红黑树则调用红黑树的判定方法
4)最后一种情况:进行循环,将链表上的所有元素进行遍历,判定是否有重复(判定标准和方法与2一样) - 需注意,触发扩容机制的条件是node数>tablesize*装填因子(0.75)不是占用table的数量。
3.18
LinkedHashSet
继承了HashSet;底层是一个LinkedHashMap,底层维护了一个数组+双向链表;根据元素的hashcode值来决定存储位置,使元素看起来是以插入顺序保存的
Map
- key不可重复,value可以重复(add返回值为一个bool变量,判断put返回值是否为空,若不为空则添加失败。而put方法会调用putval,在putval中若出现key相同的情况,会返回原来存储的value值,因此会导致put返回值不为null,从而添加失败,这也会导致key重复时value会被覆盖)
- 常用String类作为Map的key
- key和value是单射关系
- 稍微介绍一下key-value的存储方式
首先看图
1.其是存储在hashmapnode中的
2.为了方便遍历创建了entryset集合,其中有getk和getv方法,在entryset有k,v分别指向了node中的k,v
3.在entryset中,编译类型是map.entry,运行类型是node,因为hashmapnode实现了map.entry接口
3.19
Map常用方法
- put
- remove
- get
- size
- isEmpty
- clear
- containsKey
Map遍历方式
key遍历
Set keyset = map.keySet();
增强for、迭代器
value变量
Collection values = map.values();
增强for、迭代器
EntrySet遍历
Set entrySet = map.entrySet();
Set entrySet = hashMap.entrySet();
for (Object o : entrySet) {
Map.Entry m = (Map.Entry) o;
Employee emp = (Employee) m.getValue();
if(emp.getSal() > 18000){
System.out.println(emp);
}
putVal流程
这个图好啊 非常好
HashTable
K,V均不能为null(区别于Map),是线程安全的,效率较低
Collection常用工具
泛型
可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
泛型的声明及实例化
interface A<T>{}
ArrayList<Employee> employees = new ArrayList<>();
泛型类提到遍历引出的思考
Set<Map.Entry<String, Student>> entries = stringStudentHashMap.entrySet();
Iterator<Map.Entry<String, Student>> iterator = entries.iterator();
while(iterator.hasNext()){
Map.Entry<String, Student> next = iterator.next();
System.out.println(next.getKey() + " " + next.getValue());
}
遍历map用到了entryset,具体如何要联想map存储
自定义泛型类
- 普通成员可以使用泛型
- 泛型类的类型,是在创建对象时确定的
- 因此使用泛型的数组不能初始化、静态方法中不能使用类的泛型
自定义泛型接口
- 接口中,静态成员也不能使用泛型
- 泛型接口的类型,在继承接口或者实现接口时确定
补充 接口与类
- ** 接口与类相似点**
一个接口可以有多个方法。
接口文件保存在 .java 结尾的文件中,文件名使用接口名。
接口的字节码文件保存在 .class 结尾的文件中。
接口相应的字节码文件必须在与包名称相匹配的目录结构中。 - ** 接口与类的区别**
接口不能用于实例化对象。
接口没有构造方法。
接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
接口不能包含成员变量,除了 static 和 final 变量。
接口不是被类继承了,而是要被类实现。
接口支持多继承。 - ** 接口特性**
接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
自定义泛型方法
public <T> int method(int i){}
只有泛型方法被调用时类型才会确定
public void int method(E e){}
上述类型并非泛型方法
线程基础
这个框架很好,根据这个框架来梳理知识
线程简介
程序、进程与线程
程序由指令和数据构成,想要完成指令的运行需要将指令加载至CPU终,数据的读写则需要将数据加载至内存里。此外指令运行的过程中还需要用到磁盘网络等设备。这是运行一个程序的过程。程序运行时会开启一个进程,进程会用来加载指令,管理内存,管理IO的。 是资源分配的基本单位(多实例进程、单实例进程)
线程是进程的一个实体,是一个指令流,是CPU调度分派的基本单位。一个线程可以创建或者撤销另一个线程。
区别:
- 尺度
线程更小,所以多线程的程序并发性更高。进程至少包含了一个线程,每个线程执行不同的任务。 - 资源
进程是资源分配的基本单位,同一进程内多个线程共享其资源。 - 空间
进程有独立的地址空间,同一进程内多个线程共享其资源。 - 调度
线程是处理器调度的基本单位。 - 执行
线程不能单独执行,要组合成进程才能执行。一个进程至少有一个主线程。 - 切换
线程更加轻量,上下文切换成本比进程的上下文切换成本更低
并发与并行
并发:同一时刻多个任务交替执行
并行:同一时刻多个任务同时执行
线程实现
三种方式
继承Thread
实现runnable接口
下面是对调用runnable方法的一个模拟
class ThreadProxy implements Runnable {//你可以把 Proxy 类当做 ThreadProxy
private Runnable target = null;//属性,类型是 Runnable
@Override
public void run() {
if (target != null) {
target.run();//动态绑定(运行类型 Tiger)
}
}
public ThreadProxy(Runnable target) {
this.target = target;
}
public void start() {
start0();//这个方法时真正实现多线程方法
}
public void start0() {
run();
}
}
Callable实现
不同的实现方法之间有什么区别
- Thread类本身就实现了Runnable接口
- 实现Runnable接口方式更加适合多个线程共享同一资源的情况,且避免了单继承限制
线程终止
- 线程完成任务后会自动退出
- 可以使用变量来控制run方法退出的形式停止线程(通知方式)
线程常用方法
setName、getName、start、run、setPriority、getPriority、sleep、interrupt、yield、join
线程的优先级:1、5、10
interrupt
中断线程休眠
yield
线程的礼让,让出cpu让其他线程执行,但是不一定成功
join
线程插队,先执行完插队的线程后再执行原来的任务
用户线程与守护线程
- 用户线程:也叫工作线程,当前线程的任务执行结束或通知方式可以结束
- 守护线程:为工作线程服务,当所有工作线程结束时自动结束
- 常见的守护线程:垃圾回收机制
线程状态
线程包括哪些状态,状态之间是如何变化的?
- 线程包括哪些状态
在enum类State中,一共有六种
new,runnable,blocked,waiting,timed_waiting,terminated - 线程的状态是如何变化的
首先可以大致分为三个:new,runnable,terminated
一个线程被创建后可以通过start()进入可执行状态,在执行结束后进入terminated状态。
在可执行状态中在一定条件下会进入另外三种状态:
没有获取锁(synchronized,lock)blocked;
调用wait()方法waiting(notify()唤醒后切换为可执行状态);
调用sleep()方法timed_waiting(时间结束后可执行))。
线程同步
要求:保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
具体的同步方式
1.同步代码块
sychronized(Obj){}
2.在方法中声明
public sychronized void m(){}
需要注意多个线程的锁对象是同一个:
sellTickets sellTickets = new sellTickets();
new Thread(sellTickets).start();
new Thread(sellTickets).start();
new Thread(sellTickets).start();
//这种是可以的
new Thread(new sellTickets()).start();
new Thread(new sellTickets()).start();
//这种不行,纯在自娱自乐
3.lock锁
线程死锁
//下述造成了一个死锁
class DeadLockDemo implements Runnable {
static Object o1 = new Object();//保证多线程,共亭一个对象,这里使用static
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) {
this.flag = flag;
}
public void run() {
if (flag) {
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + "进入1");
synchronized (o2) {//这里获得li对象的监视权
System.out.println(Thread.currentThread().getName() + "进入2");
}
} }else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + "进入3");
synchronized (o1){
System.out.println(Thread.currentThread().getName() + "进入4");
}
}
}
}
}
释放锁
- 释放锁的操作
1.当前线程同步方法(同步代码块)执行结束
2.当前线程在同步代码块,同步方法中遇到break,return
3.在当前线程的同步方法(同步代码块)中出现了未处理的Error或Exception
4.在当前线程的同步方法(同步代码块)中执行了wait方法,当前线程暂停并释放锁 - 不会释放锁的操作
1.sleep,yield方法
2.其他线程调用了该线程的suspend方法将该线程挂起
线程通信
生产者消费者
生产者和消费者共享同一资源,且相互依赖,此时单靠synchronized无法解决,引出管程法、信号灯法
管程
public class method1 {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Productor(synContainer).start();
new Consumer(synContainer).start();
}
}
class Chicken{
int id;
public Chicken(int id){
this.id = id;
}
}
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container){
this.container = container;
}
@Override
public void run() {
//你得会生产鸡
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("push" + i);
}
}
}
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("pop" + container.pop().id);
}
}
}
class SynContainer{
Chicken[] chickens = new Chicken[20];
int count = 0;
public synchronized void push(Chicken chicken){
//如果鸡太多,不能再生产了
if(count >= chickens.length){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
chickens[count++] = chicken;
this.notifyAll();
}
public synchronized Chicken pop(){
if(count <= 0){
//不能再买了,你得去等
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Chicken chicken = chickens[--count];
this.notifyAll();
return chicken;
}
}
信号灯
public class method2 {
public static void main(String[] args) {
TV tv = new TV();
new player(tv).start();
new watcher(tv).start();
}
}
class TV {
String voice;
private boolean flag = true;
public synchronized void play(String voice){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("表演了" + voice);
this.notifyAll();
this.voice = voice;
this.flag = !this.flag;
}
public synchronized void watch(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("看了" + voice);
this.notifyAll();
this.flag = !this.flag;
}
}
class player extends Thread{
TV tv;
public player(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i % 2 == 0){
this.tv.play("1");
}else{
this.tv.play("2");
}
}
}
}
class watcher extends Thread{
TV tv;
public watcher(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
线程池简介
- 背景:经常创建和销毁线程,使用量特别大的资源
- 思路:提前创建线程放入线程池,使用时获取,用完放回池中
- 好处 :提高响应速度、降低资源消耗、便于线程管理
- 核心参数(?)
corePoolSize 核心池大小
maximumPoolSize最大线程池数
keepAliveTime线程没任务后最多保持多长时间
补充
静态代理模式
真实对象和代理对象要实现同一个接口
代理对象要代理真实角色
是线程的一个底部原理
优点:
代理对象可以实现很多真实对象不能实现的事情
真实对象可以专注于做自己的事情
Lamda表达式
- 对任何接口,如果只包含唯一一个抽象方法,则是一个函数式接口
public interface Runnable{
public static void run();
}
- 对于函数式接口可以通过lamda表达式创建该接口的对象
/**
* 加油!
* 通过这个实例同时了解内部类以及lambda表达式
*/
public class pr01 {
//2. 使用静态内部类来实现
static class acl1 implements A{
@Override
public void run(int a, int b) {
System.out.println(a + "+" + b);
}
}
public static void main(String[] args) {
acl a0 = new acl();
a0.run(1,2);
acl1 a1 = new acl1();
a1.run(1,2);
//3.局部内部类
class acl2 implements A{
@Override
public void run(int a, int b) {
System.out.println(a+b);
}
}
acl2 a2 = new acl2();
a2.run(1,2);
//4.匿名内部类
A a3 = new A() {
@Override
public void run(int a, int b) {
System.out.println(a + "+" + b);
}
};
a3.run(1,2);
//5. lambda表达式
A a4 = (a,b) -> System.out.println(a+b);
a4.run(1,2);
}
}
interface A{
//是一个函数式接口
void run(int a, int b);
//为了通用就直接加了两个函数
}
//1.常用的实现接口的方式
class acl implements A{
@Override
public void run(int a, int b) {
System.out.println(a+b);
}
}
IO
文件对象构造器
其他常见方法
delete、getName、getAbsolutePath、getParent、 length、 exists、 isFile、isDirectory
IO流
流的分类
- 操作数据单位:字节流二进制文件、字符流文本文件
- 流的方向:输入流、输出流
- 流的角色:节点流、包装流
FileInputStream
两种读取方式:
fileInputStream = new FileInputStream(filePath);
while((data = fileInputStream.read()) != -1){
System.out.print((char) data);
}
fileInputStream = new FileInputStream(filePath);
while((len = fileInputStream.read(buffer)) != -1){
System.out.println(new String(buffer,0,len));
}
第二种效率更高
文件流是一种资源,使用后一定要关闭
FileOutputStream
//对之前内容进行覆盖
fileOutputStream = new FileOutputStream(filePath);
fileOutputStream.write(info.getBytes());
//不对内容进行覆盖
fileOutputStream = new FileOutputStream(filePath,true);
fileOutputStream.write(info.getBytes());
输入流指的是数据到java程序,输出流指的是java程序到文件
节点流和处理流
- 节点流是对一个特定的数据源读写数据,如FileReader等
- 处理流是连接已经存在的流,提供更强大的读写能力,如BufferedReader
处理流的优点:
- 性能提高,增加了缓冲的方式提高了输入输出的效率
- 操作便捷,提供了方法来输入输出大批数据,更加灵活。
(PS:字节流一般是图片影像这种,二进制文件保存,字符流比如txt文件中,其输入输出较为简单,不做赘述)
对象流
序列化和反序列化:保存数据的值和数据类型/恢复数据的值和数据类型
需要实现Serializable或Externlizable
对象流的功能是提供了对基本类型和对象类型的序列化和反序列化的方法
注意事项:
- 读写顺序一致
- 需要实现接口
- 加入SerialVersionUID,提高兼容性
- 序列化对象时,默认将里面的所有属性进行序列化(static或transient除外)
- 序列化对象时,属性的类型也要实现序列化接口
- 序列化具有可继承性
网络编程
ipv4地址分类
未解决问题
- 稀疏矩阵 遇到了再说
- instanceof 这个我到现在都还没搞懂 很烦
判断的是他的运行类型 - 懒汉式和饿汉式区别(学完线程回头看)
- 第三代日期