Java可以在一处开发到处运行,即在一类操作系统上开发的程序,可以在任何操作系统上运行。
不同的操作系统有不同的JVM,java是运行在JVM上,从而实现了跨平台。
安装时建议不要单独再安装公共jre。
配置环境变量:
给程序员开的说明性文字
Java赋予了特殊含义的字符序列(单词),比如:public class static void
保留字:const ,goto
特殊值:true ,false,null
用于给类,方法,变量等起名字的字符序列。
程序运行过程中,其值不可改变的量
字面量。比如:字符串"hello",整数100,浮点数12.3,布尔值true或false,字符‘a’,空值null
自定义常量,使用关键字final
final double PI = 3.1415926
程序运行过程中,其值可以改变的量
声明格式
数据类型 变量名; 示例:int age;
初始化值
开辟内存空间并给赋值,示例:age=18;
声明并初始化
格式:数据类型 变量名 = 初始值;
示例:int age = 18;
示例:int a=1,b=2,c=3;
二进制存储数据
整数的存储,底层按照补码形式存储
正数的原码,反码,补码都相同,负数按照以上规则得出。
小数的存储
字符的存储
编码表:每个字符都有唯一对应的二进制数值
ASCII码表:英文字符,其他编码表都向下兼容此编码方式
GBK:中国编码表
Unicode的:万国码,统一码,Java本身支持的编码表
UTF-8:编码方式
基本数据类型
引用数据类型
String,数组,类,接口等
基本数据类型中,七种数值类型(除布尔类型外)可以相互转换类型。
byte、short、int、long、double、float、boolean
自动转换:数值范围小的类型转为数值范围大的类型
强制转换:数值范围大的类型转为数值范围小的类型,容易损失精度
可以强制提升某个类型
int a=1,b=2;
double c=(double)a/b;
算数运算符
加+ 减- 乘* 除/ 取余% 自增++ 自减–
自增++,自减–
单独使用时,前置后置效果一样
int a=10;
a++;//自增1
++a;//自增1
System.out.println(a);
复合使用时
后置:先使用,再自增自减。
int a = 10;
int b = a++;//后置
System.out.println(a);//11
System.out.println(b);//10
前置:先自增自减,在使用。
int a = 10;
int b = ++a;//前置
System.out.println(a);//11
System.out.println(b);//11
赋值运算符
= += -= *= /= %=
byte a = 10;
a += 10;//可以,相当于a=a+10;
a = a + 10;//编译不通过
关系运算符
结果一定是布尔类型
< > <= >= == !=
逻辑运算符
操作数都是布尔类型的,结果也是布尔类型
逻辑与& :两边都为真,才为真
逻辑或|:一边为真,就为真
逻辑非!:取反
异或^, :两边相同为假,两边不同为真。
短路与&&,:同单与
短路或||:同单或
为什么推荐使用短路与、短路或?
短路与,短路或,当通过操作符左边的表达式结果,可以直接判断出最终结果时,右边不再执行,效率高。
条件运算符(三目运算符)
格式:条件表示?结果1:结果2;
注意:结果是一个数据(不能是输出语句)。
int age=18;
String s = age>=18?"可以开车了":"好好学习";
位运算符
按照二进制位进行运算的运算符,运算效率高
&,|,~,^ ,
左移<< :左移n位,等于乘以2的n次幂。
右移>> :右移n为,等于除以2的n次幂,除不尽时,向下取整。
无符号右移>>> :底层二进制数据移动时,左边始终补0
运算符的优先级
大体的排序:算术 > 位 > 比较 > 逻辑 > 三元 > 赋值
选择结构:根据条件不同,选择性的执行某些特定代码
键盘扫描器 java.util.Scanner;
import java.util.Scanner;//导入
//....
//创建一个键盘扫描器
Scanner in = new Scanner(System.in);
//提示信息
System.out.println("请输入数据:");
//接收数据
int n = in.nextInt();//接收整数
char ch = in.next().charAt(0);//接收一个字符
String s = in.next();//接收字符串,遇到空白字符结束
String line = in.nextLine();//接收一个行字符串,遇到enter键结束。
格式一:
if(关系表达式){
语句块
}
格式二:
if(关系表达式){
语句块1
}else{ 语句块2
}
格式三:
if(关系表达式1){
语句块1
}else if(关系表达式2){
语句块2
}else if(关系表达式3){
语句块3
}else{
语句块n
}
if语句嵌套
if(年龄大18){
if(男性){
// 开车
}else{
//
}
}
格式:
switch(常量表达式){
case 常量值1:
语句块1;
break;
case 常量值2:
语句块2;
break;
case 常量值3:
语句块3;
break;
default :
语句块
break;
}
格式说明
格式
for(初始化语句①; 循环条件语句②; 迭代语句④){
循环体语句③
}
for(;;){
循环体语句块;//如果循环体中没有跳出循环体的语句,那么就是死循环
}
执行流程:①②③④②③④②③④…②
示例
for(int i=0;i<5;i++){
System.out.println(i);
}
格式
while (循环条件语句①) {
循环体语句②;
}
while(true){
循环体语句;//如果此时循环体中没有跳出循环的语句,也是死循环
}
执行顺序:①②①②①②…①
示例
int i=0;
while(i<5){
System.out.println(i);
i++;
}
格式
do {
循环体语句①;
} while (循环条件语句②);
执行顺序:① ②①②①②…②
应用场景与作用
switch语句中,用于跳出switch语句
循环语句中,跳出当前循环体
for(int i=0;i<5;i++){
if(i==2)
break;
System.out.println(i);
}
//结果:0 1
用于循环语句中,跳出当次循环,继续下一次循环
for(int i=0;i<5;i++){
if(i==2)
continue;
System.out.println(i);
}
//结果:0 1 3 4
//打印5行5列的*
//外循环控制行数
for(int i=0;i<5;i++){
//内循环控制列数
for(int j=0;j<5;j++){
System.out.print("*");
}
System.out.println();//换行
}
数组:一个存储相同类型数据的容器
数组名:数组的名字,是个变量名,遵循标识符规则,规范
元素:数组中的每个数据
索引:脚标或下标,用于访问元素的编号。0 ,1,2…
长度:数组存放的元素个数。数组的length属性
声明
格式:数据类型[] 数组名;
int[] arr;
初始化
开辟内存空间,并给初始化值
int[] arr = new int[]{1,2,3,4,5};
//简化
int[] arr = {1,2,3,4,5};
//先声明,再初始化
int[] arr;
arr = new int[5];
//声明并同时初始化
int[] arr = new int[5];
int[] arr = new int[]{1,2,3,4,5};
System.out.println(arr[0]);//获取0位置的元素
arr[0] = 11;//修改0位置的元素
int x = arr[0];
数组的遍历
int[] arr = new int[]{1,2,3,4,5};
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
动态初始化数组后,系统给出元素的默认初始值
数组是引用数据类型
堆
存放new的对象和数组
可以被所有的线程共享,不会存放别的对象引用
栈
存放变量基本类型(包含这个基本类型的具体数值)
引用对象的变量(会存放这个引用再堆里面的具体地址)
方法区
可以被所有线程共享
包含了所有static和class变量
统计相关,求总和,求均值,统计个数
查找指定元素及其位置(顺序查找,二分查找);查找最值及其位置。
冒泡排序,排序原理掌握:每轮两两比较大小,大的交换到后面,每轮比较完后最大的移动到最后位置,依次比较多轮
int[] arr = {11,2,3,44,5,66,7};
//外循环控制轮数
for(int i=0;i<arr.length-1;i++){
//内循环控制每轮比较的次数
for(int j=0;j<arr.length-1-i;j++){
//比较并交换
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
数组的反转:前后元素倒置
数组的扩容:数组空间不足时,进行扩容。通常情况扩容为原来的1.5倍或2倍。创建新数组,效率低,要减少扩容操作
数组元素的插入:通常首先需要扩容。插入元素后,保证原来元素有序性。把插入位置之后的每个元素后移一位。
数组元素的删除:把删除位置之后的每个元素前移一位,以保证原数据的有序性和连续性。
直接选择排序:每轮的第一个元素与后面的每个元素比较。把最小的与每轮的第一个交换位置。依次多轮比较。
Java提供的一个工具,里面封装一些方便操作数组的方法
本质上是一个元素是一维数组的数组
声明格式:
//声明
数据类型[][] 数组名;
示例:
int[][] arr;
初始化
静态初始化
int[][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9}};
//简化
int[][] arr = {{1,2,3},{4,5,6},{7,8,9}};
动态初始化
//规则二维表
int[][] arr = new int[2][3];//2行3列
//不规则二维表
int[][] arr = new int[3][];
arr[0] = new int[]{1};
arr[1] = new int[]{2,3};
arr[2] = new int[]{4,5,6};
二维数组元素的访问与遍历
int[][] arr = {{1,2,3},{4,5,6},{7,8,9}};
for(int i=0;i<arr.length;i++){
//遍历每个一维数组
for(int j=0;j<arr[i].length;j++){
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
面向对象:是一种编程思想,就在程序设计过程中,参照现实中事物,把事物的静态特征和动态特征抽象描述为计算机中的事件。
对象:是一个具有属性和行为具体的事物。
类:是一类具有相同属性和行为的事物的抽象。
类与对象的关系:
类是对象的模板,对象是类的实例
类是抽象的,对象是具体的
类的定义格式
【修饰符】 class 类名{
//类的成员
//属性-静态特征-成员变量
//动态特征-成员方法
}
示例
public class Student{
//成员变量
String name;
int age;
//成员方法
public void study(){
System.out.println("学习....");
}
}
通过类来创建对象
类名 对象名 = new 类名();//类名,可以看做是一种自定义的数据类型,是引用数据类型
示例
Student s = new Student();
//对象的使用
//使用属性
s.name = "tom";
s.age = 18;
//使用方法
s.study();
包名:com.atguigu.test 。当前类必须在第一行使用关键字package声明所在的包
可以避免类重名:有了包之后,类的全名称就变为:包.类名
分类组织管理众多的类
使用不同包下的类需要导入,使用关键字import 包名.类名。java.lang包下不需导入
配合权限修饰符实现权限管理
变量的分类
成员变量的声明
格式
【修饰符】 数据类型 变量名;//常用修饰符有权限修饰符,static ,final
示例
public class Student{
public String name;
public static int x=10;//属于类变量
}
成员变量的访问
实例变量访问,必须使用对象来访问
Student s = new Student();
s.name = "tom";//通过对象名.属性名 访问
类变量的访问,可以使用对象访问或类名访问(推荐方式)
Student s = new Student();
System.out.println(Student.x);//通过类名.属性名来访问
成员变量的特点
成员变量的内存分析
实例变量在堆中,静态变量在方法区中
成员变量与局部变量
成员变量 | 局部变量 | |
---|---|---|
声明的位置 | 直接声明在类的成员位置 | 声明在方法体中或其他局部区域内(方法声明上,构造方法,代码块等) |
修饰符 | public、private、static、final等 | 不能使用访问权限修饰符,可以使用final |
内存加载位置 | 堆或方法区(static修饰时) | 栈 |
初始化值 | 有默认初始化值 | 无默认初始化值 |
生命周期 | 同对象或类(static时)的生命周期 | 随着方法的调用而存在,方法调用完毕即消失 |
方法是功能代码块,为了提高代码的复用性,可以简化代码,用于描述对象的行为特征。
【修饰符】 返回值类型 方法名(【数据类型1 变量1,数据类型2 变量2,...】){
//方法体
return 【返回值】;
}
格式说明
修饰符:可以是权限修饰符,static等
返回值类型:如果方法有返回值类型必须与返回值的类型一致,没有返回值是void
方法名:标识符,xxxYyy
参数列表:可有可无,参数是局部变量
return语句:作用①返回值,②结束方法。如果没有返回值,方法体最后可以有return;
示例
public class Student{
//成员方法
public String show(){
System.out.prrintln("hello");
return "hello";
}
public static void hello(String name){
//return;
System.out.println("hello "+name);
// return;
}
}
类的外部
实例方法:对象名.方法名(实参);
Student s = new Student();
String str = s.show();//因为此方法有返回值,可以使用变量接收
s.hello("tom");//静态方法,不推荐这种方式调用
静态方法:类名.方法名(实参);
Student.hello("tom");//推荐
同一个类内部
可以直接调用本类内的其他方法。注意:静态的不能访问非静态
public class Chinese{
String name;//实例变量
static String country="China";//类变量,静态变量
public void testInstanceMethod(){}//实例方法
public static void testStaticMethod(){//静态方法
//注意:这里不能访问非静态成员
}
//实例方法
public void test(){
sout(name+"--"+country);//可以直接方法类的成员变量
testInstanceMethod();//直接访问成员方法
testStaticMethod();//实例方法中直接访问静态方法
}
}
方法调用的注意事项:
Java中,方法传参只有值传递。
方法参数是基本数据类型:形参的改变不会影响实参
方法参数是引用数据类型:形参的改变会影响实参
在同一个类中,方法名相同,参数列表不同的情况。
参数列表不同指的是:参数个数不同或类型不同或类型顺序不同。
相同类型的任意个参数。任意个参数会被封装到一个数组中,可变参数的变量实际是一个数组。
public int add(String s,int i,int...a){//表示调用方法时可以传入的参数个数任意
}
注意:一个方法中只能有一个可变参数,并且必须在最后一个。
概念:方法调用自己的情况
//求n!
public int testRecursion(int n){
if(n==1)
return 1;
return testRecursion(n-1)*n;
}
分类
注意事项:
元素是引用数据类型的数组
public class Student{
private String name;
private int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
public void printInfo(){
System.out.println("姓名:"+name+",年龄:"+age);
}
}
//主方法
//创建对象数组
Student[] stus = new Student[3];
//存储对象
stus[0] = new Student("tom",18);
stus[1] = new Student("jack",19);
stus[2] = new Student("rose",20);
//遍历数组
for(int i=0;i<stus.lenght;i++){
stus[i].printInfo();
}
概念理解:把事物内部隐藏起来,与外界隔离,对外提供安全的公共访问途径,总之,隐藏该隐藏的,暴露该暴露的。
使用权限修饰符可以实现类的封装
权限修饰符 | 本类内 | 本包内 | 其他包子类中 | 任意位置 |
---|---|---|---|---|
private | Y | |||
缺省的 | Y | Y | ||
protected | Y | Y | Y | |
public | Y | Y | Y | Y |
可以修饰外部类的是public、缺省修饰符
4种修饰符可以修饰内部类、方法、成员变量、构造器
类的封装
属性私有化,提供公共的方法用于外界访问属性
public class Student{
//属性私有化
private String name;
private int age;
//公共的getter/setter
public void setName(String name){
this.name = name;//this表示对象的引用,谁调用此方法,代表谁
}
public String getName(){
return name;
}
}
构造器的作用
初始化对象的数据
语法格式:
权限修饰符 类名(【参数列表】){
//构造器
}
格式说明
示例:
public class Student{
private String name;
private int age;
//空参构造器
public Student(){}
//带参构造器
public Student(String name){
this.name = name;
}
public Student(String name,int age){
this(name);//调用本类的其他构造器,注意这句必须在构造体内第一行
this.age = age;
}
}
满足特定标准规范的Java类
set
和get
方法。理解:类似生活中的继承,Java类也有父子关系,子类继承父类的属性和方法。
格式:使用关键字extends使两个类产生父子关系
//父类
public class Animal{}
//子类
public class Cat extends Animal{
}
继承中成员变量的特点
super.属性
来访问方法重写Override
继承中的构造方法
Java单继承限制
this关键字
当前对象的引用,表示一个对象
应用位置:
注意:静态域中不能使用this关键字
super关键字
用于在子类中方法父类的特殊关键字,不能单独使用,区别this
应用
注意:不能再静态域中使用
就近原则和追根溯源原则
在继承关系中,访问变量或方法时,首先从本类中查找调用,如果没有再去父类中查找,如果有则正常使用。
初始化方式
代码块
静态代码块,通常用于给静态变量初始值,只执行一次
class A{
static int x;
//静态代码块
static{
x=10;
}
}
构造器代码块,通常用于给实例变量初始值,每次实例化都会执行
class A{
int x;
//构造代码块
{
x=10;
}
}
类变量的初始化
类初始化的目的主要是为类变量初始化值。类的加载过程大致分为3个阶段:加载-》链接-》初始化
其中链接阶段,执行类变量的默认值初始化
在初始化阶段,执行直接显示赋值语句和静态代码块中的语句,这两部分按照代码书写的先后顺序执行
一个类的要初始化,一定会先初始化其父类,类的初始化只执行一次
实例变量的初始化
实例化的目的主要是给实例变量进行初始化值
其中2.3 根据代码的书写先后顺序来执行。
实例化语句每次创建对象都执行
概念理解:Java多态在编译期的一个类型,在运行期间表现出来的是另外一种类型的形态。
格式:
父类型引用指向子类类型的对象或接口类型引用指向实现类对象
Animal a = new Cat();//Cat是Animal的子类
//编译看左边,运行看右边
前提:
有继承关系或接口与实现类关系
有方法重写,多态才有意义
好处:
提高了代码的扩展性
public void feed(Animal a){
a.eat();
}
降低了类与类之间的耦合
多态的应用
应用在方法参数
public static void feed(Animal a){
a.eat();
}
//-------------------
main(){
Animal a = new Cat();
feed(a);
}
用在数组
Animal[] as = new Animal[3];
as[0] = new Cat();
as[1] = new Animal();
用在方法的返回值类型
public Animal buy(String name){
if("cat".equals(name))
return new Cat();
}
向上转型&向下转型
向上转型:把子类类型转换为父类类型,自动向上转型
Cat cat = new Cat();
Animal a = cat;//自动向上转型
向下转型:把父类类型转换为子类类型,强制向下转型
Animal animal = new Cat();
Cat cat = (Cat)animal;//强制向下转型
instanceof关键字:检查类的运行时类型。强制转换要保证知道原本的类型,才能强制转换,否则发生类转换异常。
Animal animal = new Cat();
if(animal instanceof Cat){
Cat cat = (Cat)animal;//强制向下转型
}
多态引用时成员的引用原则
成员变量
编译看左边,运行也看左边
虚方法:可能被重写的方法
编译看左边,运行看右边
1)静态分派:在左边父类中找到最匹配的方法
2)动态绑定:在子类中找到上一步中最匹配的方法的重写方法
非虚方法
静态方法、私有方法、final修饰的方法,编译运行都看左边
表示最终的,不可改变的
修饰的类,不能被继承
修饰的方法,不能被重写
修饰的变量,是个常量
//此类不能被继承
final class A{
public static final VALUE = 123;//静态常量
final void test(){
final int a = 10;//final修饰局部变量
//a = 20;//不可以
}
}
Java中的根类、父类。包括数组都实现了其方法
常用方法:
hashCode();
得到对象的哈希码值。默认是内存地址转换而来的一个整数。
建议子类都重写此方法,让返回值与类的成员相关,而不是与内存地址相关。
getClass();
返回对象的运行时类
new Student().getClass().getName();//返回运行时类的名称。com.atguigu.Student
toString();
返回对象的字符串表示形式。默认返回的是:”运行时类名称“+”@“+”哈希值的16进制“
建议子类都重写此方法。建议返回类的所有属性信息。
Student s = new Student();
System.out.println(s);//等同于下句
System.out.println(s.toString());
equals(Object obj);
判断两个对象是否相等,返回布尔类型。默认按照“==”进行比较,比较的是内存地址。
建议子类都重写此方法,建议用于比较对象的所有内容是否相同。
finalize()
当垃圾回收器来回收一个对象时,会调用此对象的finalize方法。
引入:如果一个类是一个更抽象的概念,可以定义为抽象类。如果类中有抽象方法,必须为抽象类。
语法格式:
//抽象类,使用关键字abstract
public abstract class Animal{
//抽象方法,使用abstract修饰
public abstract void eat();//没有方法体
}
特点:
概念理解:本质上是一种标准规范。是静态常量与抽象方法的集合
定义格式:
//接口,使用关键字interface定义
public interface Jumpable{
//接口的成员
int VALUE = 1;//静态常量,默认修饰符public static final
void jump();//抽象方法,默认修饰符 public abstract
//jdk8之后:
default void testDefault(){//默认修饰符public,实现类可以选择是否重写缺省方法。
System.out.println("缺省方法,默认方法,扩展方法");
}
static void testStatic(){//默认修饰符public,为本尽快服务的工具类方法
System.out.println("静态方法");
}
//jdk9之后:
private void testPrivate(){//为本接口缺省方法,静态方法提供服务
System.out.println("私有方法");
}
}
特点:
接口的实现
接口是用来被类实现的
//定义实现类时候可以让类实现接口
public class Cat implements Jumpable{
//重写接口中的方法
public void eat(){
System.out.println("猫跳高");
}
}
接口的多实现
public interface A{}
public interface B{}
//一个类同时实现多个接口
public class C implements A,B{}
接口支持多继承
public interface A{}
public interface B{}
//一个接口同时继承自多个接口
public interface C extends A,B{}
接口与实现类对象的多态引用
//多态:接口类型引用指向其实现类对象
Jumpable c = new Cat();
//等同于下面两句
Cat cat = new Cat();
Jumpable c = cat;
//调用功能
c.eat();//编译看左边,运行看右边
定义在一个类的内部的类,内部的类就是内部类,这个外部的类称为外部类
成员内部类
静态内部类:是一个相对独立的类,主要服务于外部类
public class Outer{
//静态内部类
static class Inner{
//不能访问外部类的非静态成员
}
}
创建对象:Outer.Inner inner = new Outer.Inner();
不能访问外部类的非静态成员
可以使用四种权限修饰符
非静态内部类:
public class Outer{
class Inner{
//不能声明静态成员,但是可以声明静态常量
}
}
创建对象:Outer.Inner inner = new Outer().new Inner();
与外部类的耦合度高,可直接访问外部类的任何成员,包括私有的
不能声明静态成员,但是可以声明静态常量
可以使用四种权限修饰符
局部内部类:定义在外部类的局部区域(方法)内。
public class Outer{
public void test(){
int a =10;//局部变量
class Inner{
//访问的外部类的局部变量,必须是个常量或等价常量
System.out.println(a);//a不可修改
}
}
}
匿名内部类
没有名字的内部类,实际是一个局部内部类。
当接口的实现类只使用一次时,可以使用匿名内部类。
格式:new 类名或接口名(){ 子类或实现类的成员}
//接口
public interface Danceable{
void dance();
}
//测试类
public class Demo{
//静态方法,参数是接口类型,表示实参应该是一个接口的实现类对象
public static void invite(Danceable danceable){
danceable.dance();
}
//主方法
main(){
//常规写法,先创建接口的实现,然后创建实现类对象,再作为实参传入invite方法。
//invite(接口的实现类对象);
//匿名内部类
invite(new Danceable(){
public void dance(){
System.out.println("跳舞");
}
})
}
}
静态的,随着类的加载而加载
修饰成员变量,类变量或静态变量,表示此类的所有对象共享的一份数据
修饰成员方法,类方法或静态方法,可以使用类名直接调用,通常用于定义工具类方法
修饰代码块,静态代码块,主要用于初始化类变量,只在类加载过程中执行一次。
修饰内部类,静态内部类
静态导入,把不同包下的类的方法静态导入,可以像本类中一样使用导入的方法
import static java.util.Arrays.*;
设计模式:针对某种开发需求,总结的一套经验
单例模式:内存中只能有某个类的一个实例,不能出现第二个。
饿汉式:
public class Singleton{
//私有的静态常量
private static final Singleton instance = new Singleton();
//私有构造器
private Singleton(){}
//公共的获取实例的方法
public static Singleton getInstance(){
return instance;
}
}
**懒汉式:**有线程安全问题,可能会得到多个实例
public class Singleton{
//私有的静态
private static Singleton instance = null;
//私有构造器
private Singleton(){}
//公共的获取实例的方法
public static Singleton getInstance(){
if(instance == null)
instance = new Singleton();
return instance;
}
}
概念理解:给程序看的一种注释。
常见注解:
自定义注解格式
public @interface MyAnnotation{}
元注解:使用在注解上的注解
* @Target 表示注解可以使用的位置
* ElementType.METHOD 用在方法上
* ElementType.FIELD 用在属性上
* ElementType.TYPE 用在类上
* @Retention 表示注解的有效范围,有效周期
* RetentionPolicy.SOURCE 只在源码中有效
* RetentionPolicy.CLASS 在字节码文件中有效
* RetentionPolicy.RUNTIME 在运行期有效
* @Inherited 表示加了此注解的类的子类同样有这个注解
* @Documented 表示在生成的API文档中有此注解信息
示例:
@Target(ElementType.METHOD) //注解可以使用的目标位置,比如类、方法、成员变量等上面
@Retention(RetentionPolicy.RUNTIME)//注解的有效周期,
@Inherited
@Documented
public @interface MyAnnotation{}
程序运行过程中发生的不正常情况,如果发生了异常不进行处理,虚拟机会终止运行。
java中使用不同的Java类表示不同的异常
异常体系:
所有的错误和异常都继承自Throwable类。
**Error错误:**表示严重的问题,一旦发生,无法处理,虚拟机只能终止。
StackOverflowError、OutOfMemoryError
Exeption:通常所说的异常处理指的这类异常,一般性问题,可以通过预防措施处理发生的异常,以保证程序可以继续运行。
编译异常:受检异常,如果此类异常没有进行处理,编译不通过
除RuntimeException类及其子类外的Exception都是编译异常
运行时异常:非受检异常,可以选择是否处理。
RuntimeException类及其子类都是运行时异常
**异常抛出机制:**当程序中发生异常时,虚拟机会抛出对应的异常类对象,从发生处逐层向方法的调用处抛出,直到主方法中还没处理,最后抛给JVM,JVM会打印异常对象中封装的异常信息,并终止运行。
异常的抛出方式:
自动抛出:异常发生后,JVM自动创建对应的异常类对象,并抛出
手动抛出:手动创建异常类对象,使用关键字throw抛出。
throw new RuntimeException("主动抛出的异常");
如果在此过程中,任何环节进行了此异常的处理,就可以保证程序的继续运行。
异常处理机制: 异常捕获
try{
//可能发生异常的代码
}catch(可能发生的异常类型1 变量1){//捕获异常
//异常1发生后的业务逻辑代码
//通常输出异常的栈追踪信息到日志文件中
}catch(可能发生的异常类型2 变量2){//后面捕获的异常类不能小于前面的
//异常1发生后的业务逻辑代码
}finally{
//一定会执行的代码,通常用于释放资源
}
异常的声明:
通常当程序中可能发生编译期异常时,必须进行异常处理,那么此时可以进异常捕获处理或者在当前方法中声明异常,表示当前方法可能有某类型异常发生,谁来调用此方法谁来处理。
使用关键字throws来在方法上声明异常
public void test() throws FileNotFoundException{}
String类表示一个字符序列,字面量“abc”也是这个类一个实例
特点:
创建字符串对象的方式
public String()
:初始化新创建的 String对象,以使其表示空字符序列。String(String original)
: 初始化一个新创建的 String
对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。public String(char[] value)
:通过当前参数中的字符数组来构造新的String。public String(char[] value,int offset, int count)
:通过字符数组的一部分来构造新的String。public String(byte[] bytes)
:通过使用平台的默认字符集解码当前参数中的字节数组来构造新的String。public String(byte[] bytes,String charsetName)
:通过使用指定的字符集解码当前参数中的字节数组来构造新的String。字符串常用方法
(1)boolean isEmpty():字符串是否为空
(2)int length():返回字符串的长度
(3)String concat(xx):拼接,等价于+
(4)boolean equals(Object obj):比较字符串是否相等,区分大小写
(5)boolean equalsIgnoreCase(Object obj):比较字符串是否相等,不区分大小写
(6)int compareTo(String other):比较字符串大小,区分大小写,按照Unicode编码值比较大小
(7)int compareToIgnoreCase(String other):比较字符串大小,不区分大小写
(8)String toLowerCase():将字符串中大写字母转为小写
(9)String toUpperCase():将字符串中小写字母转为大写
(10)String trim():去掉字符串前后空白符
(11)boolean contains(xx):是否包含xx
(12)int indexOf(xx):从前往后找当前字符串中xx,即如果有返回第一次出现的下标,要是没有返回-1
(13)int lastIndexOf(xx):从后往前找当前字符串中xx,即如果有返回最后一次出现的下标,要是没有返回-1
(14)String substring(int beginIndex) :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
(15)String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
(16)char charAt(index):返回[index]位置的字符
(17)char[] toCharArray(): 将此字符串转换为一个新的字符数组返回
(18)String(char[] value):返回指定数组中表示该字符序列的 String。
(19)String(char[] value, int offset, int count):返回指定数组中表示该字符序列的 String。
(20)static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的 String
(21)static String copyValueOf(char[] data, int offset, int count):返回指定数组中表示该字符序列的 String
(22)static String valueOf(char[] data, int offset, int count) : 返回指定数组中表示该字符序列的 String
(23)static String valueOf(char[] data) :返回指定数组中表示该字符序列的 String
(24)byte[] getBytes():编码,把字符串变为字节数组,按照平台默认的字符编码进行编码
byte[] getBytes(字符编码方式):按照指定的编码方式进行编码
(25)new String(byte[] ) 或 new String(byte[], int, int):解码,按照平台默认的字符编码进行解码
new String(byte[],字符编码方式 ) 或 new String(byte[], int, int,字符编码方式):解码,按照指定的编码方式进行解码
(26)boolean startsWith(xx):是否以xx开头
(27)boolean endsWith(xx):是否以xx结尾
正则表达式:用字符串表示的一组规则。比如:“\d{11}”
(28)boolean matchs(正则表达式):判断当前字符串是否匹配某个正则表达式
(29)String replace(xx,xx):不支持正则
(30)String replaceFirst(正则,value):替换第一个匹配部分
(31)String repalceAll(正则, value):替换所有匹配部分
(32)String[] split(正则):按照某种规则进行拆分
String类是不可变的,如果要频繁改变字符串,效率会降低,所以java提供了可变长字符序列
StringBuilder类:线程不安全,效率略高
StringBuffer类:线程安全,效率略低
常用方法
常用的API,StringBuilder、StringBuffer的API是完全一致的
(1)StringBuffer append(xx):拼接,追加
(2)StringBuffer insert(int index, xx):在[index]位置插入xx
(3)StringBuffer delete(int start, int end):删除[start,end)之间字符
StringBuffer deleteCharAt(int index):删除[index]位置字符
(4)void setCharAt(int index, xx):替换[index]位置字符
(5)StringBuffer reverse():反转
(6)void setLength(int newLength) :设置当前字符序列长度为newLength
(7)StringBuffer replace(int start, int end, String str):替换[start,end)范围的字符序列为str
(8)int indexOf(String str):在当前字符序列中查询str的第一次出现下标
int indexOf(String str, int fromIndex):在当前字符序列[fromIndex,最后]中查询str的第一次出现下标
int lastIndexOf(String str):在当前字符序列中查询str的最后一次出现下标
int lastIndexOf(String str, int fromIndex):在当前字符序列[fromIndex,最后]中查询str的最后一次出现下标
(9)String substring(int start):截取当前字符序列[start,最后]
(10)String substring(int start, int end):截取当前字符序列[start,end)
(11)String toString():返回此序列中数据的字符串表示形式
示例:
//创建StringBuilder对象
StringBuilder sb = new StringBuilder("hello");
//追加
sb.append(",").append("world").append(",").append("java");
//转换为String对象
String s = sb.toString();
Math类
public static double abs(double a)
:返回 double 值的绝对值。public static double ceil(double a)
:返回大于等于参数的最小的整数。public static double floor(double a)
:返回小于等于参数最大的整数。public static long round(double a)
:返回最接近参数的 long。(相当于四舍五入方法)Random类:用于产生伪随机数
BigInteger类:表示一个高精度的超大整数
不可变的任意精度的整数。
BigDecimal类
不可变的、任意精度的有符号十进制数。
Date类
Date date = new Date();
long time = date.getTime();//获取1970年1月1日 8:0:0 到现在毫秒值
Date date1 = new Date(毫秒值)
Calendar类
//创建对象
Calendar c = Calendar.getInstance();
//常用方法get(int a);
int year = c.get(Calendar.YEAR)
SimpleDateFormat格式化类
用于进行字符串与日期对象之间的转换。
//创建格式化类对象,可以指定格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//格式化:把日期格式化为指定格式的字符串
String strDate = sdf.format(new Date());
//解析:把指定格式的字符串解析为日期对象
Date newDate = sdf.parse("1999-09-09 11:10:10");//注意有编译期异常
JDK8的日期类
LocalDate,LocalTime,LocalDateTime类
//得到一个日期时间对象
LocalDateTime now = LocalDateTime.now();//当前系统的日期时间对象
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));//指定时区
LocalDateTime now = LocalDateTime.of(1999,9,9,10,10,10);//指定日期时间
日期,时间区间或间隔Period ,Duration
//日期间隔
Period period = Period.between(beforeDate,afterDate);
//日期时间间隔
Duration duration = Duration.between(beforeDateTime,afterDateTime);
DateTimeFormatter格式化类
该类提供了三种格式化方法:
预定义的标准格式。如:DateTimeFormatter.ISO_DATE_TIME;
ISO_DATE
本地化相关的格式。如:DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
自定义的格式。如:DateTimeFormatter.ofPattern(“yyyy-MM-dd hh:mm:ss”)
//自定义格式化类对象
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
//格式化日期对象成为指定格式的字符串
String strDateTime = dtf.format(LocalDateTime.now());
//解析:把字符串解析为日期时间对象
LocalDate parse = LocalDate.parse("2020-12-12", dtf);
System系统类
系统类中很多好用的方法,其中几个如下:
Runtime运行时类
单例模式的应用(饿汉式)
Java针对每种基本数据类型都提供了对应的引用数据类型,称为包装类
序号 | 基本数据类型 | 包装类(java.lang包) |
---|---|---|
1 | byte | Byte |
2 | short | Short |
3 | int | Integer |
4 | long | Long |
5 | float | Float |
6 | double | Double |
7 | char | Character |
8 | boolean | Boolean |
9 | void | Void |
自动装箱\拆箱
//自动装箱:把基本数据类型装箱为对应的引用数据类型
Integer i = 10;
//自动拆箱:把引用数据类型拆箱为对应的基本数据类型
int a = i;
常用API
Integer i = 10;
Integer i2 = new Integer(100);
//把字符串(数字组成)转为整数
new Integer("123");
Integer.parseInt("123");
Integer.valueOf("123");
//整数转为字符串
String str = Integer.toString(123);
String str2 = i.toString();
String str3 = String.valueOf(123);
String str4 = ""+123;
包装类的缓存
包装类的数据在缓存数值范围内时,直接从内存中取出对象,超过范围会创建新的对象
包装类 | 缓存对象 |
---|---|
Byte | -128~127 |
Short | -128~127 |
Integer | -128~127 |
Long | -128~127 |
Float | 没有 |
Double | 没有 |
Character | 0~127 |
Boolean | true和false |
示例:
Integer i1 = 10;
Integer i2 = 10;
System.out.println(i1==i2);//true 有缓存
Integer i1 = 200;
Integer i2 = 200;
System.out.println(i1==i2);//false 没有缓存
概念:本质上是只有固定几个对象的Java类,继承自Enum类。JDK1.5后
定义格式:
public enum Gender{
FEMALE,MALE
}
枚举类的要求和特点:
常用方法
1.toString(): 默认返回的是常量名(对象名),可以继续手动重写该方法!
2.name():返回的是常量名(对象名) 【很少使用】
3.ordinal():返回常量的次序号,默认从0开始
4.values():返回该枚举类的所有的常量对象,返回类型是当前枚举的数组类型,是一个静态方法
5.valueOf(String name):根据枚举常量对象名称获取枚举对象
表示一组对象的集合
常用方法
1、添加元素
(1)add(E obj):添加元素对象到当前集合中
(2)addAll(Collection extends E> other):添加other集合中的所有元素对象到当前集合中,即this = this ∪ other
2、删除元素
(1) boolean remove(Object obj) :从当前集合中删除第一个找到的与obj对象equals返回true的元素。
(2)boolean removeAll(Collection> coll):从当前集合中删除所有与coll集合中相同的元素。即this = this - this ∩ coll
3、判断
(1)boolean isEmpty():判断当前集合是否为空集合。
(2)boolean contains(Object obj):判断当前集合中是否存在一个与obj对象equals返回true的元素。
(3)boolean containsAll(Collection> c):判断c集合中的元素是否在当前集合中都存在。即c集合是否是当前集合的“子集”。
4、获取元素个数
(1)int size():获取当前集合中实际存储的元素个数
5、交集
(1)boolean retainAll(Collection> coll):当前集合仅保留与c集合中的元素相同的元素,即当前集合中仅保留两个集合的交集,即this = this ∩ coll;
6、转为数组
(1)Object[] toArray():返回包含当前集合中所有元素的数组
Iterator是一个接口,通过Collection集合的Iterator()方法可以得到一个迭代器对象。
Iterator接口中的方法主要有: hasNext(); next(); remove();
//创建集合
Collection c = new ArrayList();
//添加元素
c.add("hello");
c.add("java");
//获取迭代器
Iterator it = c.iterator();
//遍历集合
while(it.hasNext()){
Object obj = it.next();//取出下一个元素
}
可以用来遍历Collection集合或数组。遍历Collection集合时,底层实际是通过迭代器Iterator实现的。
格式
for(元素的类型 变量 : Collection集合或数组){
}
示例
Collection c = new ArrayList();
//遍历集合
for(Object obj : c){
System.out.println(obj);//打印集合中的每个元素
}
int[] arr = {1,2,3,4,5};
//遍历数组
for(int i : arr){
System.out.println(i);//打印数组中的每个元素
}
增强for与Iterable接口的关系
实现了Iterable接口的类都可以使用增强for进行遍历
public interface Iterable<T> {
Iterator<T> iterator();
}
public interface Collection<E> extends Iterable<E> {
//Iterator iterator();
}
public class ArrayList implements Collection{
//内部类
private class Itr implements Iterator<E> {
public boolean hasNext() {
return cursor != size;
}
public E next() {
}
}
@Override
public Iterator<E> iterator() {
return new Itr();
}
}
快速失败机制
当使用迭代器遍历集合的同时,使用集合的方法对集合元素进行增删等操作,会导致concurrentModifactionException并发修改异常。因为此操作会导致数据的不准确,对以后使用集合数据存在隐患。所以Java不允许此操作。
集合源码中每个实现类都有一个属性modCount,用于统计集合的修改次数,借助此属性实现快速失败机制。
List是一个Collection的子接口
特点:
特有方法:都跟索引有关
1、添加元素
2、获取元素
3、获取元素索引
4、删除和替换元素
遍历方式
List接口的典型实现类,有序的,可重复的
底层结构:数组
效率:(使用索引)查询快,增删慢
与Vector比较:ArrayList是线程不安全的,效率相对高。
源码分析:
底层结构:双向链表
效率:增删快,查询慢
特有方法:(都跟首尾相关)
特点:元素唯一,无序(有特例)
概念理解:表示一组键值对key-value
特点:key唯一,(无序)
常用方法:
1、添加操作
2、删除
3、元素查询的操作
4、元视图操作的方法:
5、其他方法
Map集合遍历
Map<Integer,String> map = new HashMap();
map.put(101,"hello");
//遍历方式一
Set<Integer> keySet = map.keySet();
for(Integer key : keySet){
String value = map.get(key);
sout(key+" = "+value);
}
//遍历方式二
Set<Map.Entry<Integer,String>> entrySet = map.entrySet();
for(Map.Entry<Integer,String> entry : entrySet){
Integer key = entry.getKey();
String value = entry.getValue();
sout(key+" = "+value);
}
HashMap实现类
特点:key是唯一,无序,key和value都可以为null
底层结构:哈希表=数组+链表+红黑树(jdk8之后)
效率:增删改查综合效率高于单纯的数组、链表
构造方法
HashMap map = new HashMap();//空参构造,没有初始容量,第一次添加元素时初始容量为16
HashMap map = new HashMap(int capacity);//指定初始容量,得到的初始容量一定是2的n次幂(比指定值大的最小的2的次幂)
存储原理
计算元素的存储位置
计算key的hashCode值,进行高低16位异或运算,再对底层数组长度取模运算,得到元素存储的索引位置。
判断是链表还是红黑树,没有重复则进行添加元素,如果是链表,在链表尾部插入。
通过equals比较key是否相同,如果相同则覆盖旧的value,并返回旧value。实现去重。
扩容机制:
LinkedHashMap实现类
TreeMap实现类
Hashtable类
Properties类
特点:key和value都是字符串
是Hashtable的子类
常用方法:
String getPropertie(String key);
void setProperty(String key , String value);
Collections 是一个操作 Set、List 和 Map 等集合的工具类。Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法:
泛型类
public class GenericsClass<T>{
T obj;
public void setObj(T obj){
this.obj = obj;
}
public T t getObj(){
return obj;
}
//public static void test(T t){}//静态成员不能使用类型变量
}
泛型类的使用
GenericsClass<String> gc1 = new GenericsClass<String>();
gc1.setObj("abc");
String s = gc1.getObj();
//注意:
//创建对象时,泛型类型参数左右两边的类型必须一致
泛型接口
public interface GenericsInter<T>{
void show(T t);
}
使用泛型接口:泛型接口不明确类型参数,实现类或子类仍然是一个泛型类
public class ClassA<T> implements GenericsInter<T>{
public void show(T t){
}
}
使用泛型接口:泛型接口明确类型参数,实现类或子类仍然不是泛型类
public class ClassAimplements GenericsInter<String>{
public void show(String t){
}
}
泛型方法
当类的成员中只有一个方法内部统一使用了一种不确定的类型时,可以把泛型声明在这个方法上,这就是泛型方法。
public class MyClass{
String a;
int b;
public void testA(String x){}
//泛型方法
public <T> T show(T t){}
}
使用场景:通常用于对数据进行反转,排序等不添加不取出数据的操作。
List<?> list = new ArrayList<Object>();
list = new ArrayList<String>();
list = new ArrayList<Animal>();
list = new ArrayList<Cat>();
使用场景:不能添加数据,适合获取数据
List<? extends Animal> list = new ArrayList<>();
//list = new ArrayList
list = new ArrayList<Animal>();
list = new ArrayList<Cat>();
使用场景:通常用于添加数据,不适合获取数据
List<? super Animal> list = new ArrayList<>();
list = new ArrayList<Object>();
list = new ArrayList<Animal>();
//list = new ArrayList();//编译失败
File类是系统中的文件或文件夹的一种抽象表示形式
构造方法
File f1 = new File("D:/test/abc.txt");//表示一个文件
File f2 = new File("D:/test/","abc.txt");//表示一个文件
File f3 = new File("D:/test/");//表示一个目录
File f4 = new File(f3,"abc.txt");//表示一个文件
常用方法
public String getName()
:返回由此File表示的文件或目录的名称。public long length()
:返回由此File表示的文件的长度。public String getPath()
:将此File转换为路径名字符串。public long lastModified()
:返回File对象对应的文件或目录的最后修改时间(毫秒值)public boolean exists()
:此File表示的文件或目录是否实际存在。public boolean isDirectory()
:此File表示的是否为目录。public boolean isFile()
:此File表示的是否为文件。public boolean createNewFile()
:当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。public boolean delete()
:删除由此File表示的文件或目录。 只能删除空目录。public boolean mkdir()
:创建由此File表示的目录。public boolean mkdirs()
:创建由此File表示的目录,包括任何必需但不存在的父目录。public String[] list()
:返回一个String数组,表示该File目录中的所有子文件或目录。 * `public File[] listFiles()` :返回一个File数组,表示该File目录中的所有的子文件或目录。
* `public File[] listFiles(FileFilter filter)`:返回所有满足指定过滤器的文件和目录。如果给定 filter 为 null,则接受所有路径名。否则,当且仅当在路径名上调用过滤器的 FileFilter.accept(java.io.File) 方法返回 true 时,该路径名才满足过滤器。如果当前File对象不表示一个目录,或者发生 I/O 错误,则返回 null。
概念理解:
数据传输的一种抽象表示,可以理解为数据传输管道,数据的流入Input指数据从其他介质中传输到内存中。数据流出Output指的是数据从内存中传输到其他介质。
IO流的分类
字节输入流InputStream
FileInputStream文件字节输入流
FileIntputStream fis = new FileInputStream("abc.txt");
int ch = fis.read();//读一个字节
byte[] bytes = new byte[1024];
int len = fis.read(bytes);//读取数据到字节数组中,并返回读取的字节个数
字节输出流OutputStream
FileOutputStream文件字节输出流
FileOutputStream fos = new FileOutputStream("abc.txt"):
FileOutputStream fos = new FileOutputStream("abc.txt",true);//追加写入
fos.write(97);//写一个字节到文件中
byte[] bytes = {97,98,99,100};
fos.write(bytes,0,2);//写字节数组的一部分到文件中
字符输入流Reader
FileReader文件字符输入流
FileReader fr = new FileReader("abc.txt");
//读取一个字符
int ch = fr.read();
//读取一个字符数组
char[] chars = new char[1024];
int len = fr.read(chars);//返回读取到的字符个数
字符输出流Writer
FileWriter文件字符输出流
FileWriter fw = new FileWriter("abc.txt");
//写一个字符到文件中
fw.write('a');
//写一个字符数组的一部分到文件中
char[] chars = {'a','b','中','国'};
fw.wrtie(chars,0,3);//写入“ab中”
//写入一个字符串
fw.write("hello world");
字符输出流默认自带缓冲区,默认大小为8k的字节数组,当缓冲满后自动把所有字符一次写入到文件中,否不会直接写入文件。
flush方法:用于刷新缓冲区,将缓冲区中的数据写入文件中。
close方法:先刷新缓冲区,再释放资源。在使用完IO流之后,一定注意调用此方法。
高效流,在创建流对象时,会创建一个8k大小的缓冲区,为了提升IO效率。
高效字节流
高效字符流
BufferedReader
String readLine();//读取一行字符串
BufferedWriter
write(String s);//写入字符串
newLine();//写入一个换行符
把字节流转换为字符流,本质上转换流是字符流,相当于字节流+编码方式
InputStreamReader
InputStreamReader isr = new InputStreamReader(new FileInputStream("abc.txt"),"GBK");//创建转换流,指定读取数据的编码方式
int ch = isr.read();//读取一个字符
OutputStreamWriter
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("abc2.txt"),"UTF-8");//创建输出转换流,指定写出字符的编码方式
osw.write('中');
序列化与反序列化
对象流
ObjectOutputStream对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt"));
//写出一个java对象
oos.write(new Student());//Student类必须实现序列化接口
ObjectInputStream
ObjectInputStream ois =new ObjectInputStreawm(new FileInputStream("oos.txt"));
//读取一个java对象
Object obj = ois.readObject();
打印流没有输入流,只有输出流
PrintStream字节打印流
println(Object obj);//多个重载方法
print(Object obj);//多个重载方法
PrintWriter字符打印流
System.in 标准输入流,从键盘读取数据
System.out 标准输出流,输出到控制台
并行与并发
线程与进程
多线程的应用场景与好处
线程调度:
CPU资源有限,系统以线程为单位,分配cpu资源,Java支持的抢占式调度方式,每个线程随机获得cpu资源。
Java提供了Thread线程类,此类的对象表示一个线程。
方式一:继承Thread类
方式二:实现Runnable接口
两种创建方式的区别
匿名内部类方式创建线程
//继承Thread类创建方式的匿名内部类方式。
new Thread(){
public void run(){
System.out.println("线程任务...");
}
}.start();
//实现Runnable接口方式的匿名内部类方式
new Thread(new Runnable(){
public void run(){
System.out.println("线程任务...");
}
}).start();
public void run() :此线程要执行的任务在此处定义代码。
public String getName() :获取当前线程名称。
public static Thread currentThread() :返回对当前正在执行的线程对象的引用。
public final boolean isAlive():测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。
public final int getPriority() :返回线程优先级
public final void setPriority(int newPriority) :改变线程的优先级
每个线程都有一定的优先级,优先级高的线程将获得较多的执行机会。每个线程默认的优先级都与创建它的父线程具有相同的优先级。Thread类提供了setPriority(int newPriority)和getPriority()方法类设置和获取线程的优先级,其中setPriority方法需要一个整数,并且范围在[1,10]之间,通常推荐设置Thread类的三个优先级常量:
MAX_PRIORITY(10):最高优先级
MIN _PRIORITY (1):最低优先级
NORM_PRIORITY (5):普通优先级,默认情况下main线程具有普通优先级。
public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
public static void sleep(long millis) :线程睡眠,使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static void yield():线程礼让,yield只是让当前线程暂时失去执行权,让系统的线程调度器重新调度一次,希望优先级与当前线程相同或更高的其他线程能够获得执行机会,但是这个不能保证,完全有可能的情况是,当某个线程调用了yield方法暂停之后,线程调度器又将其调度出来重新执行。
void join() :加入线程,当前线程中加入一个新线程,等待加入的线程终止后再继续执行当前线程。
void join(long millis) :等待该线程终止的时间最长为 millis 毫秒。如果millis时间到,将不再等待。
void join(long millis, int nanos) :等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
public final void stop():强迫线程停止执行。 该方法具有不安全性,已被弃用,最好不要使用。
public void interrupt():中断线程,实际上是给线程打上一个中断的标记,并不会真正使线程停止执行。
public static boolean interrupted():检查线程的中断状态,调用此方法会清除中断状态(标记)。
public boolean isInterrupted():检查线程中断状态,不会清除中断状态(标记)
public void setDaemon(boolean on):将线程设置为守护线程。必须在线程启动之前设置,否则会报IllegalThreadStateException
异常。
public boolean isDaemon():检查当前线程是否为守护线程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ROugPaN2-1625147661047)(F:\JavaClass\JavaSE210323SZ\复习笔记\img\image-20210421092209975.png)]
线程安全问题的原因(条件)
线程安全问题的解决:
使用关键字synchronized关键字,把操作共享资源的多条语句,捆绑成一句执行。
同步代码块
synchronized(同步锁对象){
//同步执行的代码块
}
同步方法
public synchronized void sale(){
//同步执行的代码
}
同步锁对象
单例模式懒汉式的线程安全问题解决
public class Singleton{
private static Singleton instance=null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null)
synchronized(Singleton.class){
if(instance == null)
instance = new Singleton();
}
}
}
多个线程中,一个线程执行完任务后需要通知到另一个线程来执行。这种情况就是线程间通信。
等待唤醒机制:可以实现线程间通信,使用Object类中的wait方法和notify方法。
经典生产者消费者问题:
//共享资源:工作台
public class WorkBanch {
private int max = 10;//最多放10分餐
private int num;//工作台当前的餐数
//生成快餐放到工作台
public synchronized void put() {
if (num >= max) {
try {
this.wait();//工作台满后等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "生成了一份快餐放到工作台,现在有" + ++num + "份餐");
this.notify();//随机唤醒在此监视器上等待的一个线程
}
//从工作台取餐
public synchronized void take() {
if (num <= 0) {
try {
this.wait();//没有餐,等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "取走了一份快餐,工作台现在有" + --num + "份餐");
this.notify();//随机唤醒一个等待的线程
}
}
任何线程进入同步代码块、同步方法之前,必须先获得对同步监视器的锁定,那么何时会释放对同步监视器的锁定呢?
1、释放锁的操作
当前线程的同步方法、同步代码块执行结束。
当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致当前线程异常结束。
当前线程在同步代码块、同步方法中执行了锁对象的wait()方法,当前线程被挂起,并释放锁。
2、不会释放锁的操作
线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行。
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该该线程挂起,该线程不会释放锁(同步监视器)。应尽量避免使用suspend()和resume()这样的过时来控制线程。
3、死锁
不同的线程分别锁住对方需要的同步监视器对象不释放,都在等待对方先放弃时就形成了线程的死锁。一旦出现死锁,整个程序既不会发生异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。
网络软件的架构
B/S :Browser/Server
只需要开发服务端,不需要开发客户端
对网络带宽要求高
C/S :Client/Server
安全性相对高
可以实现更好的客户端视觉效果渲染。
网络编程三要素
TCP协议和UDP协议
TCP协议
面向连接,安全性高,效率相对低
理论上连接成功后传输的数据量无限
UDP协议
非面向连接,安全性低,效率相对高
一次发生数据量有限(64k)
客户端与服务端都要使用Socket对象作为通信站点
客户端
Socket s = new Socket("127.0.0.1",12345);
//获取输出流
OutputStream os = s.getOutputStream();
//写数据
os.write("hello".getBytes());
//释放资源
os.close();
s.close();
服务端
ServerSocket ss = new ServerSocket(12345);
//接收Socket
Socket s = ss.accept();
//获取输入流
InputStream is = s.getInputStream();
//读取数据
byte[] bys = new byte[1024];
int len = is.read(bys);
System.out.println(new String(bys,0,len));//控制台输出读取到的数据
//释放资源
is.close();
s.close();
发送端
public class Send {
public static void main(String[] args) throws IOException {
//创建DatagramSocket
DatagramSocket ds = new DatagramSocket();
//创建数据包
byte[] bytes = "hello UDP".getBytes();
InetAddress ip = InetAddress.getByName("192.168.24.85");
DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length, ip, 10086);
//发送一个数据包裹
ds.send(dp);
//释放资源
ds.close();
}
}
接收端
public class Receive {
public static void main(String[] args) throws IOException {
//创建DatagramSocket对象
DatagramSocket ds = new DatagramSocket(10086);
//准备一个空包裹
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
while(true) {
//把数据接收到包裹中
ds.receive(dp);
//对方ip
InetAddress ip = dp.getAddress();
//从包裹中取数据
byte[] data = dp.getData();//数据
int length = dp.getLength();//数据长度
//控制台打印接收到的数据
System.out.println(ip.getHostAddress()+"="+new String(data, 0, length));
}
//释放资源
// ds.close();
}
}
类的加载过程
加载:把.class字节码文件加载到内存中
链接
初始化
执行静态变量的直接赋值语句;执行静态代码块中的语句
导致类的初始化操作
哪些操作会导致类的初始化?
(1)运行主方法所在的类,要先完成类初始化,再执行main方法
(2)第一次使用某个类型就是在new它的对象,此时这个类没有初始化的话,先完成类初始化再做实例初始化
(3)调用某个类的静态成员(类变量和类方法),此时这个类没有初始化的话,先完成类初始化
(4)子类初始化时,发现它的父类还没有初始化的话,那么先初始化父类
(5)通过反射操作某个类时,如果这个类没有初始化,也会导致该类先初始化
哪些使用类的操作,但是不会导致类的初始化?
(1)使用某个类的静态的常量(static final)
(2)通过子类调用父类的静态变量,静态方法,只会导致父类初始化,不会导致子类初始化,即只有声明静态成员的类才会初始化
(3)用某个类型声明数组并创建数组对象时,不会导致这个类初始化
类加载器
一个Java通过类加载器被加载到内存中,Java提供了不同的类加载器ClassLoader,对不同路径下的class文件进行加载
引导类加载器(Bootstrap ClassLoader)又称为根类加载器
加载jre/lib目录下的jar,此类加载器是由c++编写的
扩展类加载器ExtClassLoader
加载jre/lib/ext目录下的jar,此类是由Java编写,并继承自ClassLoader
应用类加载器AppClassLoader
加载classpath目录下的class,此类是由Java编写,并继承自ClassLoader
自定义类加载器
指定加载class的目录,需要继承ClassLoader
双亲委派模式
类加载器接收到一个类的加载请求时,会委托父加载器进行加载,如果父类加载器中有加载过,则加载成功直接结束,否则自己加载此类,加载成功结束,如果都加载不成功,则ClassNotFoundException
目的:防止类的重复加载;安全考虑
万物皆对象,每个类编译后生成的字节码文件,即class文件,在类加载后JVM会为每个class文件创建一个对象,也就是Class类的一个对象。这个Class对象封装了类在方法区内的数据信息,并且向Java程序员提供了访问方法区内的类数据信息的接口。
一个类加载成功后的产物是一个Class类对象
比如:Student类加载到内存中后,会得到一个Class c = Student.class
获取Class对象的方式
直接获取,类型.class
Class c = int.class
Object类的getClass方法
Class c = new Student().getClass();
Class的静态方法forName
Class.forName(“com.atguigu.test.Student”);
类加载器的loadClass方法
类加载器.loadClass(“类的全路径名称”);
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
Class对象是反射的根源。
可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的)