Java一阶段笔记
Windows不区分大小写,但是java区分大小写!
多行注释不可嵌套使用
不会就查api文档
一个Java源文件中声明为public的类只能有一个,而且类名必须和文件名一致
先输出 后换行 System.out.prinntln()就是换行
Java中字符串在class中
Long型变量必须以l结尾否则默认为int型 同理float型变量必须以f或F结尾,否则默认为double型。
Java中的char占两个字节可以输入中文 但是同样只能输入一个字符
Java中的true、false就是true、false,并不表示0、1
如果想输出\n就在前面再加一个转义字符\ 即为\\n便可输出\n
同理 想输出其他转义符号时也可在前面加上\ 例如“你不能再参加\”单身\”party了”
默认的运算都是转换成int或高于int型,例如
但是你可以在前面把它强制转换成字符然后再输出,例如
切记‘’中只能有一个字符,不能写成’a+1’(×)
当容量小的数据类型的变量与容量大的数据类型的变量做运算时,自动转换为容量大的数据类型的变量(此处的容量指的是表示数的范围而不是所占用内存的大小。例如float占4个字节而long占8个字节,但是float所能表示的数的范围却比long大。注:精度比long小,也就是值不够精确。)
强制类型转换 double d1 = 12.3; int i1 = (int)d1;(强制类型转换可能导致精度损失)
运算时默认为int和double 加L为long 加f为float
赋值时先检查有没有超出其设定值范围(默认为int double加L为long 加f为float),然后看该值能否赋给所设定的变量 例如long a = 123 (可以但是不标准)
Long a = 123456789123(×)未加L默认int但是超出了int值范围。
Byte a = 123456(×)符合int范围但是int无法赋值给byte。
59[擎1]
整形常量默认为int型,浮点型常量默认为double型
Char里面必须放一个字符 但是string字符串里可以没有内容
String是引用类型 string可以和八种基本数据类型运算 但只能做连接运算,且运算结果用string承接(默认就是string)
String无法强转为任何基本数据类型 其独立于八种基本数据类型之外 任何数据类型与string都只能做拼接运算
表示八进制时要在前面加0 十六进制时前面要加0x或0X 二进制以0b或0B开头
Java默认基础运算后结果是十进制
计算机底层都以补码的方式来存储数据 补码对于负数的运算较为简便 只需要相加而并不需要减法运算,在电路层次可以节省电路
Char c1 = 53;(ASCII码)这样的语法也是可以的但是用的很少 其等于char c1 = ‘5’;
++、--、+=、-=。。。不会改变本身变量的数据类型,byte和short使用这些符号时要小心
后++是在运算完一步之后就加上去而不是所有式子运算完之后再加上去
遇到+=直接换成=。。。+。。。
与或非 异或:相同为假,不同为真。
&(会短路)与&&(不会短路)运算结果相同,区别在于会不会短路 短路后如果后面的式子中有++则不会运算 |与||和&、&&类似 开发中常用||、&&
左移一位*2 右移一位/2 左移右边补零 右移看二进制首位(无符号右移除外)
三目运算符中间不能有语句,只能有单个字符或数字用来被承接(有语句必须要加分号,但是加了分号就会导致语句结束,会造成后半句出错)。
后++先压栈再自增 前++先自增后压栈
2.类接口名单词首大写 包全部小写 常量所有字母大写 变量如果是多单词组成首字母小写其他单词首大写
96 scanner的使用[擎2]
Switch()括号中可以放byte、short、char、int、String、枚举类型
Case后要加冒号,且case后只能声明常量,不能声明范围
For循环里定义的变量仅仅在for循环里有用
Break和continue后面不能直接跟语句,因为没啥用而且编译过不去
标识符break可以结束选定的循环
数组是引用数据类型,数组元素可以是基础数据类型也可以是引用数据类型
数组静态初始化:int[ ] num = new int[ ]{1,2,3};
数组动态初始化:String[ ] name = new String[5];
静态动态不能混着用,严格区分格式
数组默认初始化值 整形类型为0 浮点型为0.0 字符型为ASCII码0对应的空值(类似于空格)布尔型数组默认false 引用型String初始化为null
数组可以简写成int arr1 = {1,2,3,4,5},但是注意仅有静态数组可以这么写,称为推断,动态换行过后就无法推断了
二维数组的长度为他的行数
Java二维数组中可变列数,每行列数可以不同
二维数组中的外层默认值为一个指针,因为数组的值实际上就是一个指针(c)
Java和c的二维数组是有区别的,c中二维还是一维到了列就换行,但是java不一样,他每一行使用新的指针指向一个新的数组,所以可变列
在for嵌套循环中要想使用label:标签的时候就想想可不可以用布尔类型在一重嵌套中监测然后进行continue、Break或其他设置
Random的区间是【0.01,1)到不了1 所以要10-99时就random()*90+10
Arrays.binarySearch()找到的话返回的是索引,找不到的话返回的是负数
一维数组不可能有null元素,二维数组中二维的null元素可以打印
面向对象
创建一个对象=对象实例化
类具有属性和方法
只要是new的对象都存在堆空间中,类似于c的malloc,且其赋值给变量的都是地址值
定义在类中的变量叫成员变量(属性),定义在方法中的变量叫局部变量。
属性可以在前面加上权限修饰符,但是局部变量是没有这玩意的。
类的属性有默认初始化值,局部变量是没有默认初始化值的
属性加载在堆空间中,局部变量加载在栈空间中
186[擎4]
方法中可以调用属性
方法的定义就是比C语言函数多了一个权限修饰符
方法中可以调用当前类的属性和其他方法
一个.java文件只能有一个public类,但是可以有多个public方法
遇到无法使用循环的变量下标时多考虑将其(或其地址)放入指针数组然后遍历数组来进行操作或在最开始new的时候就new到指针数组里面
方法结束以后方法内的局部变量就销毁了。
方法内不能定义方法,可以调用属性、方法,可以创建变量
new Student().say(); 匿名对象相当于new一个一次性的对象,直接调用一次里面的方法就销毁。
匿名对象通常用在方法间的调用上。
方法重载:在一个类中方法同名不同参(参数类型或者顺序或者个数不同) 调用时会自动匹配适合参数的方法。
特别注意:如果找不到同类型的方法会自动类型提升。例如demo(1)但是没有demo(int)时会自动匹配demo(double)等范围更大的类型方法。
可变个数的形参虽然传入数量不等的形参时都是调用该方法(有严格匹配数量的其他方法时匹配其他方法),但是传入的形参必须是一个类型的
可变个数的形参的形参必须写在最后一个,否则编译器无法判断其他形参位置
在包含可变个数形参的方法中,最多只能声明一个可变个数形参
String和Char【】在打印的时候都不会打印地址,直接打印值(做过修改)
类型不同的引用类型变量不能相互赋值(两个不同类实例化时出现的变量不能相互赋值)
调用存在可变个数形参的方法想要调用里面的变量时就当数组用就可以了
例如 demo(String ... args){
System.out.println(args[0]);
}
220[擎5]
当方法间传递基本数据类型时,传递的是数据的值,当方法间传递引用数据类型时,传递的是存储该数据值的地址。
Private多用于给类中的属性赋值时加上限制条件(只能通过调用该类中的方法给类中的属性赋值)
四中权限修饰符都可以修饰属性、方法、构造器和内部类
修饰类的话,只能使用缺省和public
构造器的作用就是创建一个对象并可选择给对象进行初始化,类似于方法(可以在定义构造器的时候传入一个形参初始化属性)
构造器也可以重载
一旦显示定义了构造器之后系统就不再提供默认的无参构造器
构造器赋值后于显式赋值(显式和构造器同时赋值时最后值为构造器的值)
初始化->显式->构造器->对象.属性
构造器权限默认与类的权限相同
当形参与属性名字相同时给属性前加上this.关键字可特指属性(通常用于给属性赋值)
this还可修饰方法(是否可以不用new一个对象就能调用同一个类中的不同方法 可以)、调用类中的其他构造器(构造器重载时不用重复编写相同的代码),不能调用自己(更不能递归)。调用构造器时的格式为this(变量名,。。。,变量名),不用加上数据类型
构造器的调用必须放到首行,构造器的内部最多只能声明一个this()调用其它构造器
This就类似于当前对象的引用
255[擎6]
[擎7] Package关键字命名每 . 一次就是一层文件目录
同一个包下不能命名同名类或接口 不同的包可以命名同名类
Import关键字可以使用xxx.*导入该包下的所有结构 例如import java.Scanner.*,但是如果该包是xxx下的一个子包,则仍需要导入。
如果想调用不同的两个包下的同名类,则其中有一个要写全名 包名.类名
一个类可以用一个extends关键字继承其他类的所有属性方法,不能获取构造器 避免写重复代码
若父类中包含私有的属性与方法,则该属性与方法依旧可以被子类继承到。只是若在父类中没有对应的调用该属性与方法的方法时子类就无法调用子类的该属性与方法(封装性)。
一个子类只能拥有一个父类,但是一个父类可以被多个子类继承,且还支持多层继承
除Object类本身外,所有的Java类都直接或间接继承于Object类
267[擎8]
当要继承一个父类时父类中必须要有一个默认参数(无参)的构造器,否则会报错
方法重写要求:方法名和形参列表都一样
重载是在一个类里的同名不同参方法,重写是要覆盖掉父类中继承而来的同名同参方法,而且必须同名同参(若不同则相当于在子类中进行了一个方法重载)且子类的方法的权限修饰符必须不小于父类方法的权限修饰符
子类不能重写父类中声明为private的方法
子类方法的返回值必须和父类相同或者是父类的子类Object->String
注:基本数据类型都是并列关系没有子类关系那就必须要严格相同
若子父类属性名字相同时则使用super与this区分子父类中的属性,若在方法重写后用super修饰方法则用的是父类中继承来的的方法修改子类中的数据。虽说只有方法能重写属性无法重写,但是在super与this关键字的利用效果上二者相同
可以用super();调用父类中的构造器,在子类继承父类的时候可以免去代码的重复以及方便对从父类继承而来的参数的初始化
Super();的使用与this();类似,必须出现在首行。特别地,super();与this();只能出现一个且若二者都没有时,在子类的构造器中会默认调用super();(继承时不仅会继承父类的属性和方法,初始化也会一并继承)
所以在继承时父类一定要有空参的构造器
多态性:父类的引用指向子类的对象 父类引用调用子类重写方法
当使用多态的时候,调用子父类同名同参数的方法时,调用的是子类的方法,且子类中父类未声明的方法不可以调用(只能使用父类中存在的方法,但是若在子类中对该方法有重写则优先使用子类中的同名同参方法。但是特别注意:属性不具有这种特性!!!多态只跟方法有关)
多态的作用:一个函数传入的子对象的引用的形参类型不同,该函数的运行结果也不一样。(增强了函数的复用性)
287
为了避免在向下转型时出现异常,java中有instanceof关键字 格式为例如
p1 instanceof Man(p1是不是个Man对象) 该关键字返回的是true或false
如果a instanceof A 是true,a instanceof B也是true.则B是A的父类(先判断再转型)
重写方法名和形参必须要和父类中的方法完全相同且只能重写方法名形参相同的方法(String[] a与String … a除外,这两个视为同样的形参)
“==”比较的是变量所存储数据的值是否相同(包括地址数据),比较的时候两边类型要能统一(自动提升或强转)不统一的不能比(地址数据也不行)
而String的equals()方法比较的是改地址数据所对应的值是否相同。
注:Object类中的equals依旧比的是变量所存储的数据的值是否相同,String会比较地址数据所对应的值是因为他重写过
toString默认打印类名+地址值,可以重写为打印类的属性或其他值
在方法上面直接加上@Test即可单元测试该方法。不过注意:
单元测试一个类中的方法时该类的权限修饰符必须是public且该类中仅可拥有一个默认无参构造器,测试的方法必须没有返回值没有形参
304[擎9]
基本数据类型->包装类
int i = 10;
Integer num = new Integer(i);
包装类->基本数据类型
i = num.intValue();
基本数据类型->String
int i = 10;
String str = String.valueOf(i);
String->基本数据类型
int num = Integer.parseInt(str)
三元运算符的两个执行选择必须要能统一成同一种类型或者可以在自动类型提升后能转变为同一种类型
Integer内部类属性中有一个提前造好的数组里装着-128——127,当调用这个范围内的值的时候会直接从数组中取,在自动装箱的时候如果在-128——127范围内取得两个值相同则同一地址,而一旦超出这个范围则在自动装箱地址就不同了
因为有自动装箱与自动解箱,所以直接把所有的基本数据类型包装类当做基本数据类型使用就好,同理基本数据类型也可以当做包装类使用。
只有对象的引用才能调用Object类里继承来的方法(toString()),空指针不能调用任何方法,因为没有继承
属性必须要在创建实例对象后才能调用,但是static修饰的静态变量与类一起加载,不需要实例对象可以直接用类.静态变量直接修改(也可通过对象.静态变量修改,改的都是一个东西),而且静态变量在内存里只会加载一次(随着类的加载而加载),该类实例化的每个对象的静态变量地址和数据都相同(共性用static,独立个体用属性)
静态方法中只能调用静态的方法或属性。
静态方法内不能使用this关键字和super关键字。(还没有加载到堆空间,根本就不存在指针可以给你用)
操作静态属性的方法通常都是static的(不然还得去创建一个对象才能操作效率低·)
工具类中的方法通常声明为static(不用造对象就能调用方法。例如Math.random())
单例模式上来就new对象是饿汉式,什么时候用什么时候造是懒汉式
通常懒汉式比饿汉式要好,因为懒汉式只有在用的时候才会创建对象,不用的时候不会造,可以为内存节省空间。
Main方法实际上就是一个静态方法,作为一个程序的入口他也可以在控制台给字符串数组赋值
代码块如果有修饰只能用static修饰(可以缺省)
静态代码块随着类的加载不能主动调用即可执行(自动执行)而且只执行一次
非静态代码块随着对象的创建而执行(自动执行)。可以执行多次,每创建一个对象执行一次
非静态代码块可以对对象进行初始化
静态代码块可以给静态属性等进行初始化(初始化类需要直接在类中进行一些操作时会使用到代码块)静态的东西是属于类的!!!所以只会在类中加载一次,无论怎么new对象都不会在对象中重复加载。
代码块可以写多个
代码块可想象为执行完一次功能就消失,静态代码块在类加载时执行完一次后功能即消失,非静态代码块在每创建一个对象加载完一次即消失
代码块的功能可以用构造器完全替代,代码块了解即可(静态代码块有必要存在)
代码块执行先于构造器!!!但是super()优先于两者之前
顺序:super>代码块>构造器
在类中赋值的顺序:默认->显式->代码块->构造器->对象.属性/方法
final可以用来修饰类(不能被继承)、方法(不能被重写)、变量(变量变为“常量”,不能再改变)。
其中final修饰变量又分为两种 1.属性:final属性在赋值一次后就再也不能修改了。final属性的赋值方法有显式初始化,代码块中初始化、构造器中初始化
2.局部变量:(1)形参:表明形参是一个常量。调用的时候才赋值,赋值后就只能调用不能修改
(2)普通局部变量:赋完值之后只能调用不能修改
338[擎10]
当吧一个父类设计的非常抽象的时候他就成为了一个专供子类继承的抽象类(abstract类),用abstract关键字修饰后的抽象类就不能再实例化(new新类)而只能用于被继承。
抽象方法没有方法体,只能存在于抽象类中,不能被调用。便于子类对方法进行重写,且子类在继承抽象类之后必须对其中的抽象方法进行重写才可实例化(否则必须依旧将其定义为抽象类)。
包含抽象方法的类一定是抽象类,但是抽象类中可以没有抽象方法。
静态方法没有被重写这一说,他只在方法区内存在一个,更改时都是被覆盖,丢失了多态的特性。
abstract不能修饰private方法,static方法,final类与方法
abstract就是为了多态而生的
匿名类可以免去创建一个新子类,可以用类似Person p = new Person(){person的方法体(重写方法)};的模式来造一个Person类的子类对象p(多用于Person是抽象类时)然后去对他没有类名的父类引用p进行后续操作。
scan.nextXxx()类型语句可以用空格隔开来获取多个数据。
接口是为了解决Java中不能多继承而设计出来的。接口中不能定义变量,只能定义全局常量。定义全局常量时可以省略 public static final修饰符,但其实在接口里int i;还是一个全局常量。
同理,接口中在定义抽象方法时可以省略public abstract但其实还是抽象方法
接口中不能定义构造器,意味着其不能实例化。
在Java中,接口都是通过类来实现(implements)的
如果实现类覆盖了接口中的所有抽象方法,那么该类即可实例化,否则其必须定义为一个抽象类。
Java类可以实现多个接口,接口名中间用逗号隔开,接口弥补了Java单继承的局限性。
代码顺序:先写继承父类extends后写实现接口。
接口与接口之间可以多继承
接口也具有多态性
抽象类和接口都不能实例化,但是他们可以创建对象的引用,通过这一点他们就可以实现多态
接口就像是一个被加了限制的父类
352[擎11]
工厂模式是为了将创建者与调用者分离而存在的
Java8中接口中可以定义静态方法与默认方法了
接口中的静态方法只能通过接口来调用,不像类中既可以通过类名.方法也可以通过对象名.方法调用
如果子类/实现类继承的父类和接口中的方法重名且子类没重写该方法的话,默认调用的是父类中的方法(类优先原则)。但是注意只有方法有类优先原则,同名属性必须要声明清楚是哪一个的(super./接口名.)
如果想要在实现类中调用接口中的默认方法,格式为接口名.super.方法名();
内部类可以被static修饰,权限修饰符也扩展为了四个
非静态的内部类想要实例化就必须要先实例化外部类再实例化内部类,且实例化内部类的格式为 内部类名 变量名 = 外部类对象引用.new 内部类名();
异常处理分为两种 (1)try-catch-finally是自己动手解决型(finally是可选的)
(2)throws是抛给别人解决型
在try结构中声明的变量在大括号外面就不能用了
使用try-catch处理编译时异常后,在编译时就不会有异常了,但在运行时仍可能会出现异常。相当于把编译时的异常延长到运行时出现了。
finally里的代码不管出现多少异常最后都会被执行,多用于对jvm无法回收的资源的释放
一般只处理编译时异常不处理运行时异常因为处理运行时异常作用不大他只会报一堆错最后还是要回去改代码。
同一个类的非静态方法中可以调用本类的任何属性和方法,前面省略了类名.this.(因为你能使用到本类的方法证明一定实例化了这个类),不同类想要调用其他类的属性方法就要new一个对象才能调用,充分体现了面向对象的程序设计的特点。
throws只是把异常方法抛给了异常的调用者,并没有对异常进行处理
子类重写的方法抛出的异常不大于父类抛出的异常类型(为了多态)
378[擎12]
Try-catch和throws是为了用户体验而诞生的,即在程序出错时能给出合理的提示而不是一堆用户看不懂的乱码。实际问题并没有真正解决还是要等工程师修改后台的代码。
Throws是把异常向调用方法者抛出(处理异常),而throw则是手动抛出一个异常(产生异常)
手动抛出异常多用于if-else类语句中的返回错误信息处,就不用sout xxx错误了,直接抛出一个异常
Try-catch处理异常的时候如果真的捕获到了异常则try里异常语句后面的语句不会被执行,会直接执行异常对应catch里面的语句,但是try-catch外的代码还是会被继续执行。
异常可以直接视为一个对象,需要的时候new一下
接口中可以有全局常量,抽象方法,默认方法,静态方法
385
instanceof是Java中的二元运算符,左边是对象指针,右边是类;当对象是右边类或子类所创建对象指针时,返回true;否则,返回false。
由此instanceof判断子父类的对象指针时一定要从下往上。
public权限修饰符修饰的类尽管可以在一个工程中使用,但是依旧要导包,如果访问权限不够那就连导包都没用。
基本数据类型的数组存放的是基本数据类型的数据,引用数据类型的数组存放的是指针。
[擎13] 多个CPU同时执行多个任务叫并行,单个CPU通过时间片看似同时执行多个任务叫并发
多线程:程序的进程需要同时执行两个或多个任务
多线程就是一个程序跑起来后的一个进程里可以同时执行多个功能
如果使用继承Thread类的方式创建线程在双线程之后想要再多创建一个线程执行相同的方法不能重复调用同一个start(),必须要新建对象用新对象的start()。因为一个线程只能启动一次,启动之后状态就改变了,再次调用就会报错。
关于多态和方法重写简单地说就可以理解为一个就近原则,因为重写的方法离得比较近所以优先调用重写的方法。
高优先级的线程只是优先执行的概率高一些,并不是一定会先于其他线程执行,更不会只有高优先级的线程执行完了低优先级的线程才会执行。(插队的方法是join();)
你只要在打印线程名之前更改了线程名那就更改有效,无论你是把setName放在run方法外面还是里面都无所谓
在类中创建了一个其他类匿名子类的对象相当于是一个内部类的对象,该对象可以调用本类的私有属性和方法
创建多线程的两种方式:继承Thread类、实现Runnable接口
如果采用实现Runnable接口的方式创建多个线程,当run方法里的代码不同时就需要多个子实现类重写不同的run方法然后再进行线程的创建(创造实现Runnable接口的子实现类->重写由Runnable接口继承而来的run方法->实现子实现类->将子实现类的对象指针作为形参赋值给Thread的构造器->用该构造器实例化Thread类->通过Thread对象指针.start创建一个线程,并且调用在子实现类重写的run方法与main方法等并行运行)
当run中代码相同且共用数据时推荐使用实现Runnable接口的方式创建多线程。可以较好地共享数据(不用加static),并且接口可以实现多继承,而类则只能实现单继承。
开发中优先选择实现Runnable接口创建线程的方式。
继承Thread类创建线程的方式和实现Runnable接口创建线程的方式的共同点就是都需要重写run方法。
429[擎14]
多个线程处理共享的数据时会出现线程的安全问题(对共享数据处理的安全问题)。
解决线程安全的关键就在于同一时刻只能有一个线程进入逻辑处理共享数据。
解决线程安全问题的方法一:使用同步代码块synchronized(synchronized是一个关键字首字母小写)把操作共享数据的部分包裹起来。
同步监视器(锁)的要求:是任何一个类的对象指针、多个线程必须共用一把锁
操作同步代码块里对共享数据进行处理的代码时,只能有一个线程参与,其他线程等待。同步代码块里的处理相当于单线程。
使用synchronized同步代码块时不能包多了也不能包少了,要把对共享数据进行处理的代码都包进去,但是不能把对共享数据进行判断的循环包进去(想办法要把循环弄出来,但是所有对共享数据的处理包括判断都必须在同步代码块里)。否则当执行到同步代码块时只能有一条线程进入逻辑进行处理,直到循环结束都是一个线程执行完,违背了多线程设计的初衷(但也是安全的,单线程天然线程安全),解决方法就是把while(true)搬到循环外面来,里面再加一个if判断。这样的话synchronized同步代码块里的逻辑才有可能执行多次并且每个线程都有机会进入逻辑对共享数据进行操作。
436[擎15]
线程安全问题的第二种解决方式为synchronized同步方法,同步方法解决线程安全问题不需要显示地声明同步监视器(锁),默认情况下是this(当前类实例化之后对象的地址)、如果是静态的synchronized方法则默认使用的是类名.class(类对象)作为同步监视器(锁)
死锁的理解:不同的线程都在占用着对方的同步资源(锁)不放弃,并且都在等待着对方释放自己所需要的同步资源。
避免死锁的方法:少使用同步,尽量避免嵌套同步 。
解决线程安全的第三种方式是Lock锁。使用步骤为:
① 实例化一个 ReentrantLock类的对象;
② 在对共享数据进行处理的代码处用try-finally包裹起来,在try里实现对线程的上锁,
在finally里实现对线程的解锁。
③ 如果是用继承Thread类的方式创建线程,用lock锁的方式解决线程安全问题,那就必须要把ReentrantLock类的对象设置为静态的,共用一把锁而避免创建三把锁,保证lock对象唯一。
synchronized同步代码块/synchronized同步方法与lock的异同:
同:synchronized同步代码块/synchronized同步方法与lock都是为了保证线程安全而生的。
异:①synchronized同步代码块/synchronized同步方法实现了自动的上锁与解锁,而lock则需要手动地上锁与解锁。
②synchronized有代码块锁和方法锁,而lock则只有代码块锁。
③lock性能更好(在使用时建议优先考虑lock,其次是synchronized同步代码块,最后是synchronized同步方法)
解决线程安全问题的方法有三种:使用synchronized同步代码块、使用synchronized同步方法以及使用ReentrantLock类的对象的lock()、unlock()方法上锁与解锁
多线程对共享数据的处理所带来的线程安全问题中直接参与判断的的数据一定要是共享数据。不然就会出现一种仿佛是多线程处理共享数据的假象,实际上判断逻辑里的形参都是各自线程里独立的数据,各自执行固定的循环(判断)。不会造成线程安全的问题。
wait();的功能是使线程进入阻塞并释放锁。
sleep();不会释放锁,而且是阻塞固定的时间。而wait();会释放锁,并且是阻塞到被notify/notifyall唤醒为止。
notify();会唤醒一个被wait的线程,如果有多个线程被wait了,首先唤醒优先级高的那一个。
notifyall();会唤醒所有被wait的线程。
上述三个方法必须使用在同步代码块或同步方法中,且三个方法的调用者必须是同步代码块或者同步方法中的同步监视器。
上述三个方法不能使用在ReentrantLock类的对象的lock()、unlock()方法中。
wait、notify与notifyall都是属于Object类的方法。
Jdk5.0之后创建线程新增了两种方法。
第一种是通过实现Callable接口的方式创建线程,其步骤为:
① 创建一个子实现类实现Callable接口,重写(实现)该类继承于Callable接口的call方法,该方法即为创建的线程所要执行的方法。
② 实例化这个子实现类,并将其对象指针作为形参赋值给FutureTask类的构造器。使用此构造器实例化FutureTask类,创造FutureTask类的对象,其对象指针为futuretask。
③ 再将FutureTask类的对象指针作为形参传递给Thread类的构造器,并使用此构造器实例化Thread类,创建一个Thread类的对象。
④ 通过Thread类的对象指针.start();创造一个线程并调用在子实现类对象里重写的call方法与main方法并行运行。
⑤ 如果想获得call方法的返回值可以使用第二步创造的FutureTask类的对象指针futuretask.get();方法来获得返回值,其类型为一个Object对象指针(方便多态)
使用实现Callable接口创建线程的优点:
1.call方法可以有返回值。
2.call方法可以throws异常。
3.Callable是支持泛型的。
445[擎16]
最后一种创建线程的方法是采用线程池的方法,创建步骤为:
①利用父接口的引用指向子实现类的对象
ExecutiveService service = Executors.newFixedThreadPool(10);(创建了一个包含十个线程的线程池)
② 创建Runnable/Callable子实现类对象并重写run方法。
③利用service.execute(Runnable子实现类对象指针);
/service.submit(Callable子实现类对象指针);来创建线程并调用子实现类中重写的run方法与main方法并行运行。
④ 线程池使用完之后要使用service.shutdown();手动销毁
Jdk5.0线程池的出现的好处:
- 提高响应速度
- 降低资源消耗
- 便于线程管理
String:不可变的字符序列。
方法区中的字符串常量池绝不能出现两个相同的字符串。
无论是创建String类型的指针并使用字符串给其赋值还是通过new String类的对象并通过初始化给属性value赋值,value(字符串数组指针)与String类的指针指向的都是方法区中的字符串常量池中的那个不可改变的字符数组。
使用new的方法创建String类的对象实际上在内存中创建了两个对象。一个是在堆空间的new出来的对象,另一个是在字符串常量池中的char【】数组。
String中的一些底层知识:字符串常量与(任意)常量拼接还是一个字符串常量,返回的是方法区中字符串常量池中唯一的字符数组的地址。而一旦拼接中加入了变量(无论是什么变量),他就等同于了一种以new的方式来创建String类的对象,返回的是堆空间中String类的对象的地址(指针)。但是如果拼接的结果调用intern方法,则返回值一定是常量池中对应字符数组的地址。
字符串常量赋值给String类型的对象指针实质上就是把方法区中字符创常量池中对应该字符串的字符数组的地址赋值给String类型的对象指针。
459[擎17]
因为String类的对象/String对象指针指向的方法区中的字符串常量池中的字符数组里的任何东西都无法改变,所以将String的对象指针作为形参传入别的方法中时,该方法中的任何操作都只能基于形参操作,无法对原有的实参包括其对象/字符数组做出任何改变。
String是不可变的字符序列
StringBuffer是可变的字符序列,是线程安全的,效率低。
StringBuilder是可变的字符序列,是jdk5.0新增的,是线程不安全的,效率高。
480
483
如果是String或包装类需要排序,直接使用Arrays.sort();方法排序即可,因为他已经重写了compareTo();方法。
如果是自定义类需要使用排序,则可以让自定义类实现Comparable接口,重写
compareTo(obj);方法并在其中指明应该如何排序。
重写的时候返回值记住:从小到大排本对象是爹,从大到小排传进来的对象是爹。(切记类型不对时不能直接返回相减的值)
如果你对本身的排序方法不满意需要新的排序方法(从小到大->从大到小或相反)时,那就在使用Arrays.sort();方法时传入两个形参:第一个是要排序的对象数组,第二个是实现Comparator接口的子实现类的对象并且该对象里要重写compare();方法且重写的步骤和重写compareTo();方法的步骤相同,也要遵循从小到大排第一个对象是爹,从大到小排后一个对象是爹的原则。
495[擎18]
当类的对象是有限个的,确定的时候则称该类为枚举类。
集合对象的方法涉及到对比对象是否相同时都是用.equals();来比的,所以如果自定义类的对象想要利用集合类中各种需要对比对象的方法时要先重写equals方法。
数组转换成集合调用Arrays.asList()方法时,形参内只可以放对象数组比如包装类的对象数组,切莫放基本数据类型的数组。
Iterator迭代器对象有hasNext方法,常用于遍历集合时作为while循环的限制条件。在while循环里通常会写sout(iterator.next());来遍历集合。
注意使用迭代器对象的remove方法时要在next方法之后使用,否则迭代器对象指向的第一个位置是空,会报异常。同理在迭代器对象指向一个位置时不能使用两次remove方法。
528[擎19]
ArrayList、LinkedList与Vector的异同:
同:以上三个容器都实现了List接口,都用来存储有序的、可重复的数据。
异:ArrayList与Vector底层都采用Object类型的数组来存储数据,而LinkedList底层则是使用双向链表来存储数据。由此可见若是频繁地使用插入和删除操作,使用LinkedList比使用ArrayList与Vector效率更高。但是若没有其他条件的限制,ArrayList要比另外两个容器更常用,因为ArrayList是线程不安全的,Vector是线程安全的。ArrayList的效率要比Vector高。ArrayList与LinkedList包括List接口都是jdk1.2版本推出的,比较新。而Vector容器是在jdk1.0版本推出的,比较老。
使用ArrayList容器时推荐使用带参的构造器来创建集合对象,开始就定义好数组的容量,避免频繁地扩容。(创建新数组容量为原来的1.5倍->将原有数组的值复制到新数组->将新数组的地址赋值给集合对象)
多态:父类的引用指向子类的对象->就把父类的引用当子类对象用,不过只能用父类已有的方法。
可以直接打印集合对象,打印出来的是集合对象里所存储的数据。
只要在类里重写了toString方法,就可以直接打印该类的对象,打印出的是该对象里存储的数据。
535[擎20]
向Set接口的子实现类对象(集合)中添加对象时,该对象所属的类必须重写hashCode与equals方法。
向TreeSet集合中添加的对象必须是相同类的对象,才能实施对象间的排序。
TreeSet集合中对于重复的数据的判断是基于compareTo/compare方法的返回值。若返回值是零,则视为重复数据。
若要对TreeSet集合采用定制排序(Comparator接口与Compare方法),则要在创建TreeSet类的对象时将实现Comparator接口的重写过Compare方法的类的对象作为形参放入TreeSet类的构造器中。
HashMap、LinkedHashMap与HashSet、LinkedHashSet是类似的,但也有区别。区别就在于往集合中添加对象时如果HashMap、LinkedHashMap最后使用equals方法比较key相同时,就会拿要添加的value去覆盖原来的value,而不是像HashSet、LinkedHashSet那样直接添加失败。
TreeMap对对象的比较比较的是key属性。(Map类只要涉及比较、判断都是针对key属性)
562[擎21]
范型的作用就是在一开始无法确定(类中的属性、参数等)类型的时候可以不做一个类型的限制,用范型替代,寓意其可以是任何类型。但是当你在调用构造器创建该类的对象时需指明该类中的范型的实际类型。且在指明之后该对象中使用泛型的属性/参数就只能是所制定的类型(限制死了)。这样对前期的类型自由以及后期的类型安全都是一个完美的实现。
不同泛型的对象一定不能相互赋值。
通常范型不具有多态的性质,但是若是使用了通配符<?>后,则可以实现一个类多态的关系。(同类不同泛型的这些类的父类都是类名<?>)
范型使用了通配符之后的对象确实可以承接其他同类不同范型的对象,但是承接后你使用通配符的范型对象时是无法向对象里添加数据的。(只能提供遍历)
所以说通配符的加入只是为了针对同类不同泛型的一个遍历操作,而不能更改对象里的任何数据。
574[擎22]
虽然不推荐使用字节流来处理文本类型的文件,但是复制文件时使用其实不影响(但仍不推荐使用)。要注意的一点就是不要在控制台看中途的值,因为可能会由于字符被拆开而出现乱码。
用字符流处理非文本数据一定不行,即使是复制也不行!
606[擎23]
序列化对象时对象中的static与transient属性是无法序列化的。
想要获取Class类对象有三种方式,
其中使用Class类的静态方法Class clazz = Class.forName(包名.类名);最为常用。
660[擎24]
Lambda表达式的本质:创建函数式接口的子实现类对象。
方法引用也一样是为了创建函数式接口的子实现类对象,其本质就是lambda表达式。
方法引用使用场景:当使用lambda表达式时发现接口的抽象方法的形参类型和返回值类型与当前某一个已有方法的形参类型和返回值类型相同,则可以使用
对象::非静态方法
类::静态方法
类::非静态方法 来借用当前的已有方法实现子实现类对象继承而来的抽象方法。
其中 类::非静态方法 来借用类的非静态方法的模式本质上也是在调用对象的非静态方法,只是这个对象被作为与需要实现的抽象方法比较形参数量的形参之一了。
总结:方法/构造器/数组引用就是在借用方法/构造器/数组来代替lambda表达式实现由接口继承而来的抽象方法的功能并创建该接口的子实现类对象。
690[擎25]
数据库
查询语句select...from...前面的...表示字段1,字段2等,后面的...表示表名。
若字段写*则视为所有字段(用于查询所有字段)。
数据库中的null不等同于0、’ ’、“null”。
空值参与任何非<=>运算,结果都为null。
当表名与关键字、保留字冲突了的话,对该表进行操作的时候要给表名加上着重号 ` ` 。
DESCRIBE命令显示表结构。
WHERE关键字一定要在FROM的后面。
Sql中+号没有连接的作用,只能表示加法。
字符串存在隐式转换,如果转换不成功,则默认为0。
纯字符串比较不会隐式转换,会直接比较字符串。
安全等于<=>是为了能让null参与比较而引入的。如若没有使用<=>,则任何有null参与的运算结果都为null,包括null = null。
不过如果有null参与的比较还是推荐使用IS NULL/ IS NOT NULL 通俗易懂。
% 在字符中表示不确定字符的个数(包括零个)。
_ 代表一个不确定的字符。
使用模糊查询时必须使用LIKE,不能使用 = 。
在数据库中使用ORDER BY对查询到的数据进行排序,在字段后加ASC为升序排序,加上DESC为降序排序。
我们可以使用列的别名进行排序,且列的别名只能在ORDER BY中使用,不能在WHERE中使用。
数据库支持二级排序,只需要在原有排序后加上 字段+ASC/DESC 即可实现一级排序结果相同时按二级排序再来排。同理可实现多列排序。
Mysql使用LIMIT实现数据的分页显示,格式为LIMIT 偏移量,显示个数。
26[擎26]
可以给表也起别名,起了之后在SELECT/WHERE语句中必须使用表的别名。
Mysql使用JOIN...ON语法来实现内外连接的查询,格式为:
SELECT *
FROM 表1 JOIN 表2
ON 连接条件
JOIN 表3
ON连接条件;
Mysql的左外连接和右外连接使用LEFT JOIN与RIGHT JOIN实现。
总结就是:左外连接就是把左边表的对象全部都包含进来,右外连接就是把右边表的对象全部都包含进来。
当使用左/右外连接时如果查找来源表是多个表并列在一起那么左/右外连接也要并列。
Mysql字符串的索引是从1开始的。
35[擎27]
在使用CASE-WHEN-THEN语句的时候结尾想要更改字段名的话要用END “字段名”。
如果过滤条件中使用了GROUP BY 来分组,则必须要使用HAVING来替换WHERE来进行条件判断,否则会出错,且HAVING必须声明在GROUP BY的后面。
当判断条件中不涉及聚合函数时,一定把判断写在WHERE里,效率高。
COUNT(字段); 方法为计算当前字段中非NULL值的对象在结构中存在的个数,当使用了GROUP BY进行分组时,该方法就为计算当前字段在每一组中非NULL值对象存在的个数。
在使用多表查询时如果需要使用到多个外连接则分为多个查询写并且用UNION ALL连接起来。
44[擎28]
使用相关子查询时就像使用嵌套for循环一样,每次传入子查询的都是外查询中表里的一个对象,然后让这个对象遍历子查询中内表里的每一个对象进行比较或者进行其他操作。
多表查询时对一边的表进行分组之后其他的非一一对应的数据就只能用函数操控了,不能直接查询(输出),否则会报错。就算不会报错输出的值也不正确。
判断条件要针对正确的字段。
47[擎29]
MAX、MIN、AVG、SUM函数不能嵌套。
在更新表的数据的时候,不能使用从表中的某一字段中筛选出来的值作为判断条件来更新该字段的值。
58
删除表、数据库、字段用DROP,删除对象用DELETE FROM。
约束not null只能对某个列单独限定不能存储空值,不能约束表。
一个表中主键约束最多只能有一个。
主键约束约束名永远为Primary,自己起名也没用。
Mysql中其他的约束都可以用MODIFY给字段添加或删除,但是PRIMARY KEY / FOREIGN KEY 必须要用ADD/DROP来添加/删除。
在表级约束中创建主外键约束不需要使用ADD关键字。
在更改字段名时CHANGE语句的最后要加上该字段中存储的数据的数据类型。
71[擎30]
82[擎31]
删除字段要用
ALTER TABLE …
DROP COLUMN … ;
Javaweb[擎32]
Java是编译型语言,html是解释型语言。编译型语言不容许有错误,否则编译不通过。而解释型语言即使写错也不会报错。
Html由一对开始结束标签组成:<html></html>
title 表示网页的标题
meta标签设置编码方式
<br/>表示换行,是一个单标签(开始标签和结束标签是同一个且斜杠在单词后)
p表示段落标签,较于换行间隔更大。
img表示图片标签
src表示属性标签,表名图片文件的路径
h1~h6:标题标签
列表标签:
-ol有序列表
Start表示从*开始,type显示的类型:A a I i 1(字母、罗马数字、数字)
19[擎33]
-ul 无序列表
-b加粗 -i斜体 -u下划线
-sub下标 -sup上标
-<;小于 ->;大于 -&le;小于等于 -&ge;大于等于
- 空格
-a表示超链接
Href表示链接的地址
Target:
_Self在本窗口打开
_Blank在新窗口打开
_Parent在父窗口打开
_Top在顶层窗口打开
Div 层
-table 表格
-tr 行 -rowspan 行合并
-td 列 -colspan 列合并
-th 首行中的列
- form 表单
-action 表示表单发送的目的地。
-method表单的发送方式
-input 输入数据 使用时一定要指明type属性。
Type为text、password时,发送后台的属性为name。
Type为radio(单选)、checkbox(复选)时,发送后台的属性为name值为value。
Type = “submit”为提交按钮。
Type = “reset”为重置按钮。
Type = “button”为普通按钮。
-select下滑列表
-option列表值
-textarea 多行文本框
CSS盒子模型:
- border 边框
- margin 间距
- padding 填充
text-align:center; 文本居中
js是一门弱类型的语言。
Day54
Serverlet接口中的setAttribute/getAttribute方法所存储/取出的数据和sessionId是绑定的,所以如果换一个浏览器或者清空一次cookie(失去了sessionId)也就无法再取到Attribute中的值。
37
45
51
54
IOC是为了降低代码间的耦合度而产生的
反射+注解+设计模式 = 框架
BeanFactory是懒汉式的创建对象方法(在加载配置文件的时候不会创建对象),只有调用getBean()方法的时候才会去创建对象。
ApplicationContext是饿汉式的创建方法,会在读取配置文件时就把对象创建好。
6
DI注入方法有两种格式:
① set注入
② 有参构造注入
通过xml配置文件可以完成对象的创建和属性的注入。
Javaweb
Head标签里通常包含三部分内容:
1.title标签(标题)
2.css样式
3.js代码
Body标签里包含着网页显示的内容
标签拥有自己的属性
1.基本属性 如bgcolor(背景颜色)
2.事件属性 可以直接设置事件响应后的代码
属性必须要有值,属性的值必须要加引号
常用的特殊字符:
1. <
<
2. > >
3. 空格
a标签是超链接标签
href属性设置跳转的地址
target属性设置哪个目标进行跳转(以本页跳转还是另起新页面跳转)
部署web工程到服务器上可以将工程文件(夹)放到Tomcat下的webapps文件夹中,也可以使用xml部署。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)