Java初级
1.变量
1.基本数据类型(7种):整数类型(byte(占一个字节)、short(占2个字节)、int(占4个字节)、long(占8个字节))浮点型(float(占4个字节)、double(占8个字节))布尔类型(Boolean)
按精度从高到低排列:byte<short<int<long<float<double
2.‘ s’:表示一个字符注意和ASCII码表和Unicode表联系起来; “ s”:表示一个字符串
3.转义字符 “\” ,后接一个或者多个字符
4.标识符由数字、字母、下划线、美元标识符且不可以用数字开头,严格区分大小写
5.变量是存在生命周期的,根据生命周期可以分为“成员变量”和“局部变量”,
成员变量:在**类**体中定义的,在整个类中都是有效的,他可以分为静态变量(static)和实例变量。
局部变量:在==类的方法==中定义的,方法内部定义“{” 与 “}”之间的代码中声明的变量,只能在当前的代码块中有效。
6.幅值运算符:= 将右边的值赋给左边的值
算数运算符:+ - * /(取整数) %(取余数)
自减自加运算符:++ – (必须是整数或者浮点型变量)
++a :表示在使用变量的时候先进行自加(减)再使用a值**(工程中一般采用这个,因为他不会消耗系统内存)**
a++:表示在使用变量的时候先使用a的值再进行自加(减)
比较运算符: > < == != >= <= 比较结果输出的是boolean值
逻辑运算符:&(&&)逻辑与 、||(逻辑或)、!(逻辑非)
三元运算符:c=条件式?值a:值b 若条件的值为true时,那么将值a赋值给c,否则就将值b赋值给c
7.隐式类型转换:从低类型向高类型的转换(系统将自动执行)
显示类型转换:把高精度的变量转换成低精度的变量,必须使用显示类型转换(强制类型转换) (类型名)要转换的值 例:int a =(int)45.23
8.代码注释:“//”:单行注释
“/**/”:多行注释
“/** */”:文档注释
2.流程控制
1.复合语句:复合语句是由整个块区为单位的语句,所以又被称为块语句。复合语句是由开括号“{”开始,闭括号“}”结束。
2.条件语句:
2.1 if语句:
只有一条语句的时候可以保留后面的{}也可以省略
if(boolean表达式){//条件为真时开始执行
语句序列
}
2.2 if…else if 语句:
if(表达式){
语句序列
}else if{
语句序列
...
}else if{
...
}else{
...
}
2.3 switch多分支语句
switch (表达式){ //括号里面的表达式和case后面的表达式的数据类型必须保持一致
case 常量值1:
语句块1
break;
...
case 常量值n:
语句块n
break;
default;
语句块n+1:
break;
}
2.4循环语句
**2.4.1 while语句 :当条件为真的时候执行执行语句,条件为假的时候跳出{}语句 ** 带有迭代器的while循环的快捷键:itit
while(条件表达式){
执行语句
}
//死循环
while(true){//一般和break一起使用
...
}
注意事项和细节说明:
1.循环条件是返回一个布尔值。
2.4.2 do…while语句 在执行语句中必然被执行一次
do
...
{
执行语句
}while(条件表达式);
2.4.3 for循环语句 表达式2为false的时候跳出循环
for(表达式1;表达式2;表达式3){//记得用“;”
语句序列
}
表达式1:初始化表达式,负责完成变量的初始化
表达式2:循环条件表达式,值为Boolean类型的表达式,指定循环条件
表达式3:循环后操作表达式,负责修整变量,改变循环条 件
注意事项和细节说明:
1.循环条件是返回一个布尔值的表达式
2.for( ;循环判断条件; ) 中的初始化和变量迭代可以写到其他的地方,但是两边分号不能省略。
3.循环初始化可以有多条初始化语句,但是要求类型是一样,并且中间用逗号隔开,循环变量迭代也可以有多条迭代语句,中间逗号隔开。
4.最多不要超过3层for嵌套。
2.4.3.1 foreach 增强for循环语句 快捷键 : I
for(元素类型 元素名 :集合名或数组名){
访问元素;
}
2.4.4 跳转语句 break、continue 和 return
break: 在switch语句中是用来终止循环操作,在for、while、do…while循环中是用来强制退出当前循环
注意事项:
1.break语句可以指定退出哪层
2.break后指定的到哪个标签就退出到哪里
3.在实际的开发当中,尽量不要使用标签
4.如果没有指定break,默认退出最近的循环体
continue:用于终止本次循环执行下一次循环
return:当return用在方法时,表示跳出方法,如果用在main,表示退出程序
3.字符串
3.1 声明字符串
字符串必须有“ ”(双引号) 引起来
3.1.1 字符串的创建
String s = new String("xxx");
3.2字符串的连接
两个字符串之间可以通过 +“ ”+ 进行链接
3.3 获取字符串信息
3.3.1获取字符串的长度
str.length()//索引从0开始,length()-1:为最后一个
3.3.2 获取指定索引位置的字符串
str.charAt(int index);
3.4 字符串操作
3.4.1获取子字符串
substring(int beginIndex)//获取字符串从指定的索引开始直到结束(在字符串中空格占一个索引)
substring(int beginIndex,int endIndex)//获取字符串从指定开始的索引到结束的索引
3.4.2去除字符串中的空格
str.trim()
3.4.3字符串替换
str.replace(char oldChar,char newChar)
3.4.4判断字符串的开始和结尾
str.startWith(String prefix)//用于判断该字符串是不是以这个以这个指定的字符串作为前缀,prefix指的是作为前缀的字符
str.endsWith(String suffix)//用于判断改字符串是不是以指定的字符串结束,suffix作为后缀的字符串
3.4.5 判断字符串是否相等
str.equals(String otherstr)//如果两个字符串的长度和字符(区分大小写)是一样的就会返回true
str.equalsIgnoreCase(String otherstr)//忽略字符串的大小写来比较两个字符串是否是相等的,返回Boolean值
3.4.6按照字典的顺序比较两个字符串
str.compareTo(String otherstr)
按照字典顺序前一个str的顺序比后一个str顺序前面的话就返回的结果是负整数,反之输出的结果为正整数,如果两个字符串相等时就返回0
3.4.7 字母大小写的转换
str.toLowerCase()//将字符串转换成小写
str.toUpperCase()//将字符串转换成大写
3.4.8字符串分割
str.split(String sign)//sign是分割字符串的分隔符也可以是正则表达式
str.split(String sign,int limit)//limit为限制每个的次数
分隔符:“|”、“,” 、 “=”、 “,|=”
3.4.9 格式化字符串
str.format(String format,Object...args)//format:格式化字符串 args:格式字符串中由格式说明符引用的参数
str.format(Local1,String format,Object...args)//1:为格式化过程重要应用的语言环境,如果1为null则,则不进行本地化
//例子
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
d = sdf.format(date);//格式化时间
String s = String.format("%te",date);//通过format的方法对日期进行格式化
常规转换符
转换符 | 说明 | 示例 |
---|---|---|
%b %B | 结果被格式化为布尔类型 | true |
%h %H | 结果被格式化为散列码 | A058976545 |
%s %S | 结果被格式化为字符串类型 | “abcgvw” |
%c %C | 结果被格式化字符类型 | ‘a’ |
%d | 结果被格式化为十进制整数 | 0326 |
%o | 结果被格式化为八进制整数 | 11 |
%x %X | 结果被格式化为十六进制 | 4b1 |
%e | 结果被格式化为用计算机科学计数法表示的十进制数 | 1.700000e+01 |
%a | 结果被格式化为带有效位数和指数的十六进制浮点数 | 0X1.C0000000001P4 |
%n | 结果为特定平台的行分隔符 | |
%% | 结果为字面值‘%’ | % |
3.4.10正则表达式(运用的时候上网址查)
元字符 | 正则表达式 | 意义 |
---|---|---|
. | . | 代表任意的一个字符 |
\d | \\d | 代表0-9的任意一个数字 |
\w | \\w | 代表可用作标识符的字符,但不包括$ |
… | … | … |
限定修饰符 | 意义 | 示例 |
---|---|---|
? | 0次或者1次 | A? |
* | 0次或者多次 | A* |
+ | 一次或者多次 | A+ |
… | … | … |
3.4.11 字符串生成器 StringBuilder/Tostring()
StringBuilde中可以实现动态的添加、删除、插入字符串操作
append(content)//向字符串生成器中添加内容
insert(int num arg)//num为插入的位置,arg为要插入的字符串
delete(int start,int end)//删除输入开始的索引到结束索引
Tostring方法
bf.toString()//将bf生成字符串
4.数组
4.1一维数组的创建和使用
4.1.1 一维数组的创建
数组元素的类型 数组名字 [];
int arr[];//声明是int型数组,数组里面每个元素都是int型
数组元素类型[] 数组名字;
String [] str;//声明是字符串类型的数组
数组名字= new 数组元素的类型[数组元素的个数];
arr = new int[5];//定义数组的长度是5
数组元素类型 数组名= new 数组元素类型[数组元素的格式];
int month =new int[12];
//初始化一维数组
int arr[] = new int[]{1,2,3,4,5,...};
int arr[] = {1,2,3,45,65,34,9};
int arr2[]={12,23,4,12,...};
4.1.2 二维数组的创建和使用
数组元素类型 数组名字[][];
int myarr[][];
数组元素类型[][] 数组名字;
String [][] myarr;
//二维数组的初始化
int[][] myarr={{12,1},{23,2},...};
4.2 数组的基本操作
4.2.1 数组的遍历
int arr[][]=new int[][]{{1},{2,3},{4,5,6}};
for(int i=0;i<arr.length;i++){}//普通的for循环一维数组
for(int i =0 ;i<arr.length ; i++){//遍历二维数组
for(int j = 0;j<arr[i].length;j++)
System.out.println(arr[i][j])
}
for(int x[]:arr){}//增强for循环
4.2.2 填充替换数组的元素
fill(数组类型[] arr,数组类型 value);
fill(数组类型[] arr,int fromIndex,int toIndex,数据类型 value);
4.2.3 对数组进行升序
Arrays.sort(object);//object是指要进行升序排列的数组
4.2.4 对数组进行复制操作
copyOf(arr,int newlength);//arr是要进行复制的数组, newlength指的是新复制数组的长度,如果新的数组长度大于原来的则超出的部分如果是char型就用null代替,如果是整型数组就用0还代替
copyOfRange(数组名字, int fromIndex,int toIndex);//复制指定的开始索引位置和结束的索引位置
4.2.5 数组的查询
binarySearch(Object[] a,Object key);//a:表示的是要搜索的数组,key:指的是要搜索的值
binarySearch(Object[] a,int fromIndex,int toIndex,Object key);//指定在数组的范围内进行搜索值
4.3 数组排序算法
4.3.1 冒泡算法
基本思想:他排序数组元素的过程总是将小的数往前放,大的数组往后放
算法实现(两层for循环)
public void sort(int[] arr){//声明方法,构造方法
for(int i=0;i<arr.length-1;i++){
for(int j=0;j<arr.length-i-1;j++){
if(arry[j]>arry[j+1]){
int temp =arry[j];//把第一个元素保存到临时的变量中
arry[j]=arry[j+1];//将第二个元素复制给第一个元素
arry[j+1]=temp;//把临时变量中的元素复制给第二个元素
}
}
}
}
//主方法
public class BubbleSort(){
public static void main(String[] args){
int[] arry={1,3,6,234,5};//初始化数组
BubbleSort sorter = new BubbleSort;//直接创建BubbleSort类的对象
sorter.sort(arry);//调用定义的方法
}
}
4.3.2直接选择排序算法
基本思想:就是将指定位置与其他数组元素分别对比,如果满足条件两个就进行位置的交换
算法实现:
public class SelectSort {
public static void main(String[] args){
int[] arry={1,3,6,234,5};
SelectSort sorter = new SelectSort();//直接创建SelectSort类的对象
sorter.sort(arry);
for (int i = 0; i < arry.length; i++) {
System.out.println(arry[i]);
}
}
public void sort(int [] arry){//构造方法
for(int i=0;i<arry.length;i++){
int index=i;
for(int j=i+1;j<arry.length;j++){
if(arry[j]>arry[index]){
index=j;
}
}
//交换在位置arry.length-i和index(最大值)上的两个数
int temp= arry[i];//把最后一个索引的位置上的数赋值到临时变量中
arry[i]=arry[index];//把两者比较中的最大值赋值给最后一个索引的位置
arry[index]=temp;//然后把临时变量给索引的那个位置
}
}
}
4.3.3 反转排序
基本思想:就是讲第一个元素与倒数第一个元素互换,第二个元素与到时第二个元素互换,…
算法的实现:
public class ReverseSort {
public static void main(String[] args){
int[] arry={1,3,6,234,5};
ReverseSort sorter = new ReverseSort();//直接创建ReverseSort类的对象
sorter.sort(arry);
for (int i = 0; i < arry.length; i++) {
System.out.println(arry[i]);
}
}
public void sort(int[] arry){
int temp;
for(int i=0;i<arry.length/2;i++){
temp=arry[i];
arry[i]=arry[arry.length-1-i];
arry[arry.length-1-i]=temp;
}
}
}
5.类和对象
5.1对象
java中万事万物都是对象,创建对象的方法,在java语言中通过new操作符来创建对象,new出来的对象都是放在**堆(heap)**中
Car car =new Car();//创建一个汽车的对象
5.1.1对象的引用
类名 对象的引用名称
Book book;//在同一个类中
Book book = new Book();
5.1.2对象的比较
两种对象的比较方式分别为“==”和equal();两种方法存在着本质上的区别,eqauls()的方法时String类中的方法它是用于比较两个对象所指的字符内容是否是一样的,而“= =”的方法时比较的是两个对象的地址值是否是一样的。
5.1.3对象的销毁
1.对象的引用超过其作用范围,这个对象被视为垃圾
2.将对象赋值为null
5.2 类
类就是同一事物的统称,最基本的要素就是有属性和方法,其中方法表示的是做某些事情的方式,方法其实就是函数,只不过java习惯把函数称为方法。
方法的基本组成包括:方法的名称、参数、返回值和方法体
public int getReault(){
//...
return 1;
}
getResult 就是方法名称、()里面是返回的参数 return是值返回的值 注意:方法的返回值必须和方法参数的类型保持一致。void 无返回值。
5.2.1 方法的重载
每个重载的方法都有独一无二的参数列表
重载的条件
1.方法的名称必须相同 |
---|
2.参数列表必须不同(个数不同、类型不同、参数类型排列顺序不同等) |
3.方法的返回类型可以相同也可以不相同 |
4.仅仅返回类型不同不足以成为方法的重载 |
5.重载是发生在编译时的,因为编译器可以根据参数的类型来选择使用哪个方法 |
注意事项和使用细节
1)方法名:必须相同
2)形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名**无要求**)
3)返回类型:无要求
5.2.2 方法的重写
方法的重写是描述子类和父类之间的,而方法的重载指的是同一类中的。
- 父类通常是被定义为抽象类,在抽象类中给出了一个方法的标准,而不给出实现的具体流程。
class Fruit{
public void eat(){
System.out.printl('eat fruit');
}
}
class Apple extends Fruit(){//extends继承父类
@override//重写的标志
public void eat(){
System.out.printl('eat apple');
}
}
构成重载的条件
//参数类型不同,构成重载
public int add(int,int)
public double add(double,double)
public double add(double,int)
//参数个数不同,构成重载
public int add(int,int)
public int add(int)
//参数顺序不同,构成重载
public double add(double,int)
public double add(int,double)
在同一个类中我可以重写你的方法但是我必须要保证的是参数类型不同,参数个数不同,参数顺序不同三种情况中的一种即可构成重载。
方法的重写原则
1.重写的方法必须和父类保持一致,包括返回值类型,方法名,参数列表也都一样 |
---|
2.重写的方法可以用==@override==注解来标识 |
3.子类中的重写的方法和访问的权限不能低于父类中方法的访问权限 |
4.子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类,比如 父类的返回类型是 Object,子类方法返回类型是String |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQfuLkIM-1628148194892)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20210311092133879.png)]
5.2.3 成员变量
在java中前面由Class关键字饰的一般是定义类,而前面是private关键字修饰的一般是成员变量,在类中定义的。
成员变量 = 属性 = field(字段)
5.2.4成员方法
java中成员方法对应于类对象的行为。可以调用其他成员方法或类成员变量。
权限修饰符 返回值类型 方法名(参数类型 参数名){
...//方法体
return 返回值;//若方法无返回值可以使用void关键字
}
成员方法的调用小结
1.当程序执行到方法时,就会开辟一个独立的空间(栈空间)
2.当方法执行完毕,或者执行到return语句时,就会返回。
3.返回到调用方法的地方。
4.当main方法(栈)执行完毕,整个程序退出。
成员方法的好处
1.提高代码的复用性
2.可以将实现的细节封装起来,然后工其他用户来调用即可
成员方法注意事项和使用细节
1.访问修饰符(作用是控制 方法使用范围)
2.返回类型
1)一个方法最多有一个返回值[返回多个值的话可以用数组的形式]
2)返回类型可以为任意类型,包含基本类型或引用数据类型(数组,对象)
3)如果方法要求有返回数据类型,则方法体中最后执行语句必须为return值;而且要求返回值类型必须和return的值类型一致或者兼容。
4)如果方法名是void,则方法体中可以没有return语句,或者只写return;
5)方法名遵循骆驼峰命名法,最好见名知义,表达出该功能的意思即可。
3.参数列表
1)一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开,比如getSum(int n1,int n2)
2)参数类型可以为任意类型,包含基本类型或引用类型,比如printArr(int [] [] map)
3)调用参数的方法时,一定对应着参数列表传入相同类型或者兼容类型!
4)方法定义时的参数称为形式参数,简称形参;方法调用时的参数称为实际参数,简称实参,实参和形参的类型要一致或者兼容、个数、顺序必须一致
5)方法体里面可以写完成功能的具体语句,可以为输入、输出、变量、运算、分支、循环、方法调用3,但是方法里面不能再定义方法。
4.方法调用的说明
1)同一个类中的方法调用:直接调用即可
2)跨类中的方法A类调用B类方法:需要通过对象名调用。比如对象名.方法名(参数);
3)特别说明一下:跨类调用和方法的访问修饰符相关。
基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参
引用数据类型传递的是地址(传递也是值,但值是地址),可以通过形参影响实参
5.2.4 权限修饰符
访问控制权限又被称为封装,public>protected>default>private
private | default | protected | public | |
---|---|---|---|---|
同一类 | √ | √ | √ | √ |
同一包中的类 | √ | √ | √ | |
子类 | √ | √ | ||
其他包中的类 | √ |
√:表示的是可以进行访问
注意事项和使用细节:
1.修饰符可以用来修饰类中的属性,成员方法以及类
2.只有默认(default)和public才能修饰类,并且遵循上述访问权限的特点
3.成员方法的访问规则和属性完全一样
5.2.5 局部变量
在成员方法中定义的变量,有效的范围从该变量的声明开始到该变量的结束为止(成员方法花括号的范围)
public String getName(){//定义一个成员方法
int id=0;//定义一个局部变量
setNme("Java");
return id +this.name
}
5.2.6 this
在java语言中规定使用this关键字来代替**本类对象的引用,this关键字可以隐式的用于调用成员方法和成员变量**
必须将this定义的变量放在第一个位置。
private void setName(String name){
this.name=name;//this调用的是成员变量的方法
}
this小结:简单说,那个对象调用,this就代表那个对象
5.2.6.1super
super是指向父类的一个引用,可以使用super.对象也可以使用super(参数)。
关键字 | this | super |
---|---|---|
调用方式 | 调用==本类中==的属性、构造函数、方法 | 调用==父类中==的属性、构造函数、方法 |
调用位置 | 构造函数的第一行,其他可以自行指定 | 构造函数的第一行,其他可以自行指定 |
调用次数 | 一个函数只能调用一次 | 一个函数只能调用一次 |
注意!!!! this和super关键字不能同时使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PDQUfDvI-1628148194894)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20210311092054916.png)]
5.2.7 类的构造方法
构造方法的特点如下:
1.构造方法没有返回值;
2.构造方法的名称要与本类的名称相同;
3.这里的构造方法没有返回值与普通的没有返回值需要void关键字不同,构造方法中不需要添加void
4.只有在类中没有定义任何的构造方法时,编译器才会在该类中自动的构造一个不带参数的构造方法。
public book(){//piblic为构造方法的修饰符 book为名称
//...构造方法体
}
5.2.8 static
由关键字static修饰的变量、常量和方法称作静态变量、常量和方法
注意:1.在成员方法中不可以使用this关键字
2.在静态方法中不可以直接调用非静态(先有静态才非静态)
3.java规定不能将方法体内的局部变量声明为static
//如何定义
权限修饰符 static 返回值类型 静态变量名 = xxxx
//如何调用
类名.类变量名
5.2.8.1 静态变量(类变量 )
1.不管static变量在哪里,共识
(1)static变量是同一个类所有对象共享
(2)static类变量,在类加载的时候就生成了
2.什么是类变量
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
3.类变量和类方法
1)什么时候需要用类变量
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如定义学生类,统计所有学生共交多少钱。Student(name,static fee)
2)类变量与实例变量(普通变量)的区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
3)加上static称为类变量或者静态变量,否则称为实例变量/普通变量/非静态变量
4)类变量(类方法)都可以通过 类名.类变量名 或者对象名.类变量名 来访问,但java的设计者推荐使用 类名.类变量名方式访问。(前提是 满足访问修饰符的权限和范围)
5)实例变量不能通过 类名.类变量名 方式访问
6)类变量是在类加载时就初始化了,也就是说,及时你没有创建对象,只要类加载了,就可以使用类变量了
7)类变量的生命周期是随着类的加载开始,随着类的消亡而销毁。
4.静态 方法
1)类方法经典的使用场景:当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。比如:工具类中的方法utils
2)在程序员的实际开发中,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如打印一维数组,冒泡排序,完成某个计算任务等
5.类变量和类方法的使用细节和注意事项:
1)类方法和普通方法都是随着类加载而加载,将结构信息存储在方法区:
类方法中无 this 的参数
普通方法中隐含着this的参数
2)普通方法可以通过类名调用,也可以通过对象名调用
3)普通方法和对象有关,需要先创建对象再通过对象名调用,比如 对象名.方法名(参数),不能通过类名调用
4)类方法中不允许使用与对象名有关的关键字,比如 this和super。普通方法(成员方法)可以。
5)类方法(静态方法)中只能访问静态变量和静态方法
6)普通成员方法,既可以访 非静态成员,可以访问静态成员
总结: 静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员(必须遵守访问权限)
6.深入理解main方法:
解释main方法的形式:public static void main(String[] args){}
1)main方法是虚拟机调用
2)java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
3)java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
4)该方法接收String类型的数组参数,该数组中白村执行java命令是传递给锁运行的类的参数,接收参数
5)java执行的程序 参数1 参数2 参数3
特别提醒:
1)在main()方法中,我们可以直接调用main方法所在的类的静态方法和静态属性
2)但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。
5.2.8.2 静态代码块
public class StaicBlock{
static{
System.out.println("i am a hensome man!!!")
}
}
静态代码块随着类的加载而执行,因此很多时候只需要进行一次的初始化操作放在static代码块中进行
代码块的注意事项和使用细节:
1)static代码块也叫静态代码块,作用就是对类进行初始化,而且他随着类的加载而执行,并且**只会执行一次**。如果是普通代码块,每创建一个对象,就执行一次。
2)类是什么时候被调用的(类什么时候被加载?)?
1.创建对象实例时(new)
2.创建子类对象实例,父类也会被加载
3.使用类的静态成员时(静态属性,静态方法)
3)普通代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。如果只是使用类的静态成员时,普通代码块并不会被执行。
总结: 1.static代码块是类加载时,执行,只会执行一次
2.普通代码块是在创建对象时调用的,创建一次,调用一次
4)创建一个对象时,在**一个类**调用顺序是:(重点,难点):
1.调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
2.调用普通代码块和普通属性初始化(注意:普通代码块和普通属性初始化调用的优先级是一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
3.调用构造方法
5)无参构造器的最前面其实隐含了 super() 和 调用普通代码块,静态相关代码块,属性初始化,在类加载时,就执行完毕,因此是优先于 构造器和普通代码块执行的。
class A{
public A(){
//这里隐藏的执行要求
//(1)super();
//(2)调用普通代码块
System.out.println("ok");
}
}
6)我们看一下创建子类对象时(继承关系),他们的静态代码块,静态属性初试化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
1.父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
2.子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
3.父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
4.父类的构造方法
5.子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
6子类的构造方法
7)静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
5.2.9 final
final的意思是最后的、最终的,它可以修饰类、属性和方法
1.在某些情况下,程序员可能有以下需求,就会使用到final:
1)但不希望类被继承时,可以使用final修饰
2)但不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。
3)当不希望类的某个属性的值被修改,可以使用final修饰
4)当不希望某个局部变量被修改,可以使用final修饰
2.final使用细节和注意事项
1)final修饰的属性又叫常量,一般 用XX_XX_XX(大写)来命名
2)final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置
-
定义时:如public final double TAX_RATE = 0.08;
-
在构造器中
-
在代码块中
3)如果final修饰的属性是静态的,则初始化的位置只能是:
-
定义时
-
在静态代码块中
4)final类不能继承,但是可以实例化对象
5)如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
6)一般来说,如果一个类已经是final类了,就没有必要在将方法修饰成final方法
7)final不能修饰构造方法(即构造器)
8)final和static往往搭配使用,效率更高,不会导致类加载。底层编译器做了优化处理。
9)包装类(Integer,Double,Float,Boolean等都是final类),String也是final类
5.3 递归
1.递归重要规则
1.执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2.方法的局部变量是独立的,不会互相影响,比如n变量
3.如果方法中使用的是引用类型变量(比如数组,对象),就会共享改引用数据类型的数据
4.递归必须向退出递归的条件逼近,否则就会是无限递归,出现StackOverflowerError,死龟了)
5.当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用就将结果返回给谁,同时当方法执行完毕或者返回是该方法也就执行完毕。
5.4 可变参数
注意事项和使用细节:
1.可变参数的实参可以为0个或者任意多个。
2.可变参数的实参可以为数组。
3.可变参数的实质就是数组。
4.可变参数可以和普通的参数一起放在形参列表,但必须保证可变参数在最后
5.一个形参列表中只能出现一个可变参数。
5.4 作用域
1.在java编程中,主要的变量就是属性(成员变量)和局部变量
2.我们说的局部变量一般是指在成员方法中定义的变量。Cat类:cry
3.java中作用域的分类:
-
全局变量 :也就是属性,作用域为整个类体Cat类:cry eat等方法使用属性
-
局部变量:也就是除了属性之外的其他变量,作用域为定义他的代码块中。
4.全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值。
注意事项和使用细节:
1.全局变量(属性)和局部变量可以重名,访问时遵循就近原则。
2.在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
3.属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。局部变量,生命周期较短,伴随着他得代码块的指行而创建,伴随着代码块的结束而销毁。即在一次方法调用过程中。
4.作用域范围不同
-
全局变量/属性:可以被本类使用,或其他类使用
-
局部变量:只能在本类中对应的方法中使用
5.修饰符不同
-
全局变量/属性可以加修饰符
-
局部变量不可以加修饰符
5.5 构造方法/构造器
注意事项和使用细节:
1.一个类可以定义多个不同的构造器,即构造器重载。
比如:我们可以再给Person类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄
2.构造器名和类名要相同
3.构造器没有返回值
4.构造器是完成对象的**初始化**,并不是创建对象
5.在创建对象时,系统会自动调用该类的构造方法
6.如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),比如Dog(){}
,使用javap指令反编译看看
7.一旦定义了自己的构造器,默认构造器就覆盖了,就不能再使用默认的无参构造器,除非显示的定义一下,即:Dog(){}写(这点很重要)
6.包装类
6.1 Integer
Integer是int的封装
- 构造方法
Integer number = new Integer(7);//以int型变量作为参数来获取Integer对象
Integer number = new Integer(7);//以String型变量作为参数来获取Integer对象
toBinaryString();//转化成2进制的字符串
toHexString();//转化成16进制的字符串
toOctalString();//转化成8进制的字符串
-
常用的方法
方法 返回值 功能描述 equals(Object IntegerObj) Boolean 比较两个对象的值是否是相等的 toString() String 返回一个表示Integer值的String对象 parseInt(String str) Int 将字符串转化成等等价的整数值 3.常量
Integer类提供了4个常量
MAX_VALUE:表示int类型可以获得的最大值231-1
MIN_VALUE:表示int类型可以获得的最大值-231
SIZE:用来以二进制补码的形式表示int值得位数
TYPE:表示基本类型的int的Class实例
- Integer 直接转换的范围-128~127,超出范围则会new Integer(xxx)
6.2 Boolean
Boolean是boolean的封装
1.构造方法
Boolean b = new Boolean(true);//创建表示value参数的Boolean对象
Boolean b = new Boolean("ok");//创建表示String变量作为参数,创建对象
2.常量
Boolean提供3个常量
TRUE:对应基值是true的Boolean对象
FALSE:对应基值是false的Boolean对象
TYPE:基本类型Boolean的Class对象
6.3 Byte
Byte是byte的封装
1.构造方法
byte mybyte=45;//创建一个变量
Byte b = new Byte(mybyte);//创建一个对象将变量赋值到b中
2.常量
Byte类提供了4个常量
MAX_VALUE:表示Byte类型可以获得的最大值231-1
MIN_VALUE:表示Byte类型可以获得的最大值-231
SIZE:用来以二进制补码的形式表示byte值得位数
TYPE:表示基本类型的byte的Class实例
6.4 Character
Character 是char的封装
1.构造方法
Character(char value);
Character mychar= new Character('s');
2.常用的方法
方法 | 返回值 | 功能描述 |
---|---|---|
toUpperCase(cahr ch) | char | 将字符参数转换成大写 |
toLowerCase(char ch) | char | 将字符参数转换成小写 |
isUpperCase(char ch) | boolean | 判断指定的字符是否是大写字符 |
isLowerCase(char ch) | boolean | 判断指定的字符是否是小写字符 |
… | … | … |
3.常量
Character提供了大量表示特定字符的常量
CONNECTOR_PUNCTUATION:返回byte值,表示Unicode规范中的常规类别“Pc”
UNASSIGNED:返回byte值,表示Unicode规范中的常规类别“Cn”
TITLECASE_LETTER:返回byte值,表示Unicode规范中的常规类别“Lt”
6.5 Double
Double是double的封装
1.构造方法
Double(double value);//基于double参数创建的Double类对象
Double(String str);//构造一个新分配的Double对象,表示用字符串表示的double类型的浮点值
2.常用方法
方法 | 返回值 | 功能描述 |
---|---|---|
isNaN | boolean | 判断double是不是非数字(NaN)值 |
compareTo(Double d) | int | 判断的是两个值如果相等的话就返回0,如果调用的值小于d那么就返回负值,如果调用的值大于d那么久返回正数 |
… | … | … |
3.常量
Double提供了一下的常量
MAX_EXPONENT:返回int值
MIN_EXPONENT:返回int值
NEGATIVE_INFINITY:返回double值
POSITIVE_INFINITY:返回double值
6.6Number
抽象类Number是BigDecimal、BigInteger、Byte、Double、Float、Integer、Long和short的父类。
6.7 StringBuffer
- StringBuffer 的直接父类 是 AbstractStringBuffer
- StringBuffer 实现类Serializable,即StringBuffer的对象可串行化
- 在父类中 AbstractStringBuffer 有属性 char[] value,不是final 改value 数组存放 字符内容,引出存放在堆中的
- StringBuffer 是一个final类,不能被继承
- 因为StringBuffer 字符内容存在 char[] value, 所有在变化(增加/删除)不用每次都更换地址(即不是每次创建新对象),所以效率高于String。
6.7.1 String、StringBuffer 和 StringBuilder的比较
- StringBuilder 和StringBuffer 非常类似,均可代表可变字符的序列,而且方法也一样
- String:不可以变字符序列,效率较高(增删)、线程安全。
- StringBuffer:可变字符序列、效率最高、线程不安全
- StringBuilder:可变字符序列、效率最高、线程不安全
- String使用注意说明:
String s = “a”; //创建一个字符串
s += “b”;//实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串“ab”。如果多次执行这些改变串内容的操作,会导致大量副字符串对象留在内存中,降低效率,如果这些造作放到循环中,会极大影响程序的性能=》结论:如果我们对这String 做大量修改,不要使用String
6.7.2 String、StringBuffer 和 StringBuilder的选择
使用原则,结论:
- 如果字符串存在大量的修改操作,一般使用StringBuffer 或 StringBuilder
- 如果字符串存在大量的修改操作,并且单线程的情况下,使用StringBuilder
- 如果使用字符串存在大量的修改操作,并在多线程的情况,使用 StringBuilder
- 如果我们字符串很少修改,被多个对象引用,使用String,比如配置信息等
7.数字处理类
7.1 数字格式化
java中没有格式化的数据遵循以下原则:
如果数据绝对值大于0.001并且小于10000000,java将以常规小数形式表示 |
---|
如果数据 绝对值小于0.001并且大于10000000,java将以科学计数法表示 |
DecimalFormat是NumberFormat的一个子类,用于格式化十进制数字(先创建出一个DecimalFormat的一个对象,然后对象再调用format方法进行格式化)
DecimalFormat myFormat = new DecimalFormat();
str=myformat.format("1224544")
7.2 数学运算
java中提供了基本运算的Math类,类中定义了数学运算方法如:三角函数方法、指数函数方法、对数函数方法、平方根方法等,还定义了E和PI
7.2.1 Math类
调用的方法
Math.数学方法
Math.PI
Math.E
7.2.2 常用数学运算方法
1.三角函数方法
方法 | 功能描述 |
---|---|
public static double sin(double a) | 返回的是正弦函数 |
public static double cos(double a) | 返回的是余弦函数 |
public static double tan(double a) | 返回的正切函数 |
public static double toRadians(double a) | 将角度转化成弧度 |
public static double toDegress(double a) | 将弧度转换成角度 |
2.指数函数方法
方法 | 功能描述 |
---|---|
public static double exp(double a) | 用于获取e的a次方 |
public static double log(double a) | 用于获取自然对数 |
public static double sqrt(double a) | 用于获取a的平方根 |
public static double pow(double a,double b) | 用于获取a的b次方根 |
3.取整函数方法
方法 | 功能描述 |
---|---|
public static double ceil(double a) | 向上取整 |
public static double floor(double a) | 向下取整 |
public static double rint(double a) | 返回与参数最近的整数如果两个同为整数且同样接近那么返回偶数 |
public static int round(double a) | 四舍五入 |
4.取最大值、最小值、绝对值
方法 | 功能描述 |
---|---|
public static double max(double a , double b) | 取a,b两个中最大的值 |
public static double min(double a,double b) | 取a,b两个中最小的值 |
public static int abs(int a) | 取a的绝对值 |
3. 随机数
7.3.1 Math.random()方法
random()的方法随机生成大于等于0.0小于1.0的随机double型数字,即==0<=Math.random<1.0==,可以对其处理可以生成任意范围的随机数,如下:
(int)(Math.Random()*n)//返回大于等于0小于n的任意随机数
m+(int)(Math.Random()*n)//返回大于等于m小于m+n的随机数
7.3.2 Random 类
实例化一个Random()对象,创建一个随机数生成器
Random r = new Random();
4、占位符
- %s,%d,%.2f,%c 称为占位符
- 这些占位符由后面变量来替换
- %s 表示后面由 字符串 来替换
- %d 是由 整数 来替换
- %.2f 表示用 小数 来替换,替换后,只会保留小数点后两位,并且进行四舍五入的处理
- %c 使用 char 类型来替换
5、将数组中的字符串数据转化成数字的形式
//数组里的字符串转换成数字的几种方法
var arr = ['1', '2', '3']
arr.map(Number); //结果:[1, 2, 3]
['1', '2', '3'].map(returnInt); // [1, 2, 3]
// 意料之中的结果
['1', '2', '3'].map( str => parseInt(str) );
// 也可以使用简单的箭头函数,结果[1, 2, 3]
//数组中把数字转换成字符串
[1, 2, 3].map(String) //结果['1', '2', '3']
8.接口、继承、多态
8.1继承
基本思想:基于某个父类的扩展,制定出一个新的子类,子类可以继承父类的所有属性和方法,也可以增加父类没有的方法和属性,或者直接重写父类中的某些方法。
继承的关键字:extends,如果使用了extends那指定了继承关系
public class 子类 extends 父类{}
在子类中可以通过super()方法来调用父类的成员方法和构造方法,但是子类**没有权限调用父类中用private**关键字修饰的方法,子类还可以修改方法的返回值类型
注意:在实例化子类对象的时候,父类**无参构造的方法将被自动构造,但有参构造的方法并不能被自动调用,只能依赖super关键字显示的调用**父类的构造方法
继承的使用细节:
1.子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
2.子类没有继承父类的构造器,但必须调用父类的构造器,完成父类的初始化
3.当创建子类对象时,不管使用子类的哪个构造器,默认的情况下总会调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中使用super去指定使用父类的那个构造器完成对父类的初始化工作,否则,编译不会通过
4.如果希望指定去调用父类的某个构造器,则显示的调用一下:super(参数列表)
5.super在使用时,必须放在构造器的第一行(super只能在构造器中使用)
6.super() 和 this() 都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器中
7.java所有类都是Object类的子类,Object是所有类的基类
8.父类构造器的调用不限于直接父类!将一直往上追溯到Object类(顶级父类)
9.子类最后只能继承一个父类(指直接继承),即java中是单继承机制。可以有多个兄弟姐妹但是只能有一个父亲
10.不能滥用继承,子类和父类之间必须满足 is-a的逻辑关系。
11.所有的构造器如果没有this指定的话都有一个super()指向父类的无参构造
查找关系来返回信息的步骤:
1)首先先看子类是否有该属性
2)如果子类有这个属性,并且可以访问,则返回信息
3)如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息)
4)如果父类还没有就按照3)的规则,继续找上级父类,直到Object
8.2 多态
多态指的就是同一个行为具有多个不同的表现形式。是指一个类的**实例(对象)**在不同的方法中有不同的表现形式。封装和继承是多态的基础,也就是说,多态只是一种表现形式而已。
多态: 方法或者对象具有多种形态,是OOP的第三大特征,是建立在封装和继承基础之上。
8.2.1 对象的多态
1)一个对象的编译类型和运行类型可以不一致
2)编译类型在定义对象的时候就已经确定了,不能改变。
3)运行类型是可以改变的。
4)编译类型看定义时 = 号的 左边,运行类型看 = 号的 右边
8.2.2 注意事项和使用细节
1)多态的前提是:两个对象(类)存在继承关系
2)属性没有重写之说,属性的值看编译类型
3)instance of 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型。
8.2.1 java的动态绑定机制
1.当调用对象方法的时候,该方法和该对象的内存地址/运行类型绑定
2.当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
8.3 Object 类
Object类是所有类的父类,提供了以下重要的方法:
getClass().getname;//他返回的是对象想执行时的Class实例(就是Class后面的类名,也就是对象)返回的就是对象名
//例子
public class ObjectInstance(){
public String toString(){
return getClass().getName();//返回 ObjectInstance
}
}
8.3.1 == 和 equals 的对比
**==**是一个比较运算符
1)== : 既可以判断**基本类型,又可以判断引用类型**
2)==: 如果判断基本类型,判断的是值是否相等。示例:int i = 10; double d = 10.0;
3)==: 如果判断引用类型,判断的是地址是否相等,即判断是不是一个对象
equals方法
4)equals:是Object类中的方法,只能判断引用类型,Object类中默认判断的是地址(对象)是否相等,但是子类往往重写equals方法,才用于判断内容是否相等。
instanceof 用来判断对象的运行内存是否一样
8.3.2 hashCode方法
- 提高具有哈希结构的容器的效率
2)两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
3)两个引用,如果指向的是不同对象,则哈希值是不一样的
4)哈希值主要是根据地址号来的!不能完全将哈希值等价于地址
8.3.3 toString方法
1.基本介绍:
默认返回:全类名+@+哈希值的十六进制,子类往往重写toString方法,用于返回对象的属性信息
2.重写toString方法,打印对象或者拼接对象时,都会自动调用该对象的额toString形式。
3.当直接输出一个对象时,toString方法会默认调用。System.out.println(moster);就会默认调用 monster.toString();
8.3.4 finalize方法(在开发中基本用不到,主要是在面试中)
1.当对象被回收时,系统会自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
2.什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用干垃圾回收机制来销毁该对象,在销毁该对象之前,会先调用finalize方法。
3.垃圾回收机制的调用,是由系统来决定的(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制。
8.3.5 DeBug操作
F7(跳入):跳入到方法内
F8(跳过):逐行执行代码
shift + F8(跳出):跳出方法
F9(resume,执行到下一个断点)
8.4对象类型的转换
8.4.1 向上(下)转型
向上转型代表了父类与子类之间的关系,其实父类和子类之间不仅仅有向上转型,还有向下转型,他们转型的范围是不一样的。
向上转型 :通过子类对象(小范围)转化为父类对象(大范围),这种转换是自动完成的,不用强制
//父类名称 对象名 = new 子类名称();//右侧创建一个子类对象,把它当做父类看待使用。
Animal animal = new cat();//创建一只猫当做动物看待没问题。
注意事项:向上转型一定是安全的。从小范围转向大范围,从小范围的猫转向了大范围的动物。
向上转型方法规则如下:
1)可以调用父类中的所有成员(需遵守范文权限)
2)但是不能调用子类的特有的成员
#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
3)最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法。调用规则和前面所讲的规则是一样的。
向下转型:通过父类(大范围)实例化子类对象(小范围),这种转型不是自动完成的,需要强制指定**(其实就是还原的动作)**
//子类名称 对象名 = (子类名称)父类对象;//将父类对象【还原】成为本来的子类对象。
Cat cat =(Cat)animal;//本来是猫,已经被当做是动物现在要把它还原回去原本的猫。
注意事项:
1.必须保证对象本来创建的时候,就是猫,才能向下转型成为猫。
2.如果对象创建的时候本来不是猫,现在非要向下转型,那么就会报错。
8.5 抽象类与接口
abstract是定义抽象类的关键字,如果把接口形容为狗这个物种,那么抽象类可以说是毛发是白色、小体品种,而实现类可以是具体的类,比如说是博美、泰迪等。
public abstract class Test(){
abstract void testAbstract();//定义抽象类方法
}
1.什么是抽象类?
父类方法不确定性的问题,考虑该方法设计为抽象(abstract)方法,所谓抽象方法就是没有实现的方法,那所谓的没有实现就是指没有方法体,当一个类中存在抽象的方法时,需要将该类声明为abstract类。一般来说抽象类会被继承,由子类来实现。
2.抽象类有以下的特征
1.如果一个类中有抽象方法,那么这个类一定是抽象类,也就是说,使用关键字**abstract** 修饰的方法的一定是抽象方法,具有抽象方法的类一定是抽象方法。实现类方法中只有具体的方法的实现。
2.抽象类中不一定只有抽象方法,抽象类中也有具体的方法,你可以自己去选择是否实现这个方法。
3.抽象类中的约束不像接口那么严格,可以在抽象类中定义 构造方法、抽象方法、普通属性、方法、静态属性和静态方法
4.抽象类和接口一样不能被实例化,实例化只能实例化具体的类
3.抽象类的使用细节和注意事项:
1)抽象类不能被实例化
2)抽象类不一定要包含abstract方法。也就是说抽象类可以没有abstract方法。
3)一旦类包含了abstract方法,则这个类必须声明为abstract
4)abstract只能修饰类和方法,不能修饰属性和其他的。
5)抽象类可以有任意的成员(抽象类本质还是类),比如:非抽象方法、构造器、静态属性等等
6)抽象类方法不能有主体,即不能实现。如图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28HxSeo9-1628148194895)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20210318094226259.png)]
7)如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非他自己也声明为abstract类。
8)抽象方法**不能使用private、final和static**来修饰,因为这些关键字都是和重写相违背的。
8.6 接口
接口是抽象类的延伸,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体,用关键字==interface==,在创建的时候应该选择implement这个选项。
public interface drawTest{
void draw();//接口内的方法,省略abstract关键字
}
interface 接口名{
//属性
//方法(1.抽象方法 2. 默认实现方法 3. 静态方法)
}
class 类名 implements 接口{
自己的属性;
自己的方法;
必须实现的接口的抽象方法
}
**小结: **
1、在jdk7.0之前接口里的所有方法都没有方法体,即都是抽象的方法。
2、jdk8.0后接口可以有静态方法,默认方法,也就是说接口中可以有方法的具体体现
一个类实现一个接口可以使用implements关键字
public class Parallelogram extends Quadrangle implements drawTest{
...//
}
8.6.1 接口的特征
- interface 接口是一个完全的抽象类,他不提供任何方法的实现,只是会进行方法的定义。
- 接口只能使用两种范文修饰符,一种是public,他对整个项目可见;一种是default缺省值,它只有具有包访问权限
- 接口只提供方法的定义,接口没有实现,但是接口可以被其他类实现。也就是说,实现接口类的需要提供方法的实现,实现接口使用**implements**关键字表示,一个接口可以有多个实现
- 接口不能被实例化,所以接口中不能有任何的构造方法,你定义构造方法编译会出错
- 一个类可以同时实现多个接口,放在implements后面用“ ,”隔开
class 类名 implements 接口1,接口2,...,接口n;
8.6.2 接口的使用细节
1)接口不能被实例化
2)接口中所有的方法是public方法,接口中的抽象方法,可以不用abstract修饰
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JYROSlP5-1628148194896)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20210320142707311.png)]
3)一个普通类实现接口,就必须将该接口的所有方法实现
4)抽象类实现接口,可以不用实现接口的方法。
5)一个类同时可以实现多个接口
6)接口中的属性,只能是final,而且是 public static final 修饰符。比如:int a =1;实际上是public static final int a = 1;
- 接口中属性中方法的访问形式:接口名.属性名
8)接口不能继承其他类,但是可以继承多个个别的接口
interface A extends B,C{}
9)接口的修饰符 只能是public 和默认,这点和类的修饰符是一样的。
小结(继承和接口的区别):
1)当子类继承了父类,就自动的拥有父类的功能
2)如果子类需要扩展功能,可以通过实现接口的方式扩展
3)可以理解 实现接口 是对 java单继承机制的一种补充。
4)接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性。
接口的价值主要在于:设计,设计好各种规范(方法),让其他类去实现这些方法。即更加的灵活。
5)接口比继承更加灵活
接口比继承更加灵活,继承是满足is - a 的关系,而接口是只需满足 like - a 的关系
6)接口在一定的程度上实现代码的解耦
9 创建内部类
9.1 内部类的基本介绍
定义内部类就是直接在一个类中定义一个类,内部类拥有内部类的访问权,他可以调用外部类的所有成员方法以及成员变量,内部类还可以定义在方法和作用域内部,这种被称为局部内部类,除此之外,还有匿名内部类、内部类可以实现java的多重继承,和其他的类的初始化方式一样都是用关键字==new==
类的五大成员是哪些? {属性、方法、构造器、代码块、内部类}
public class OuterClass(){ //定义一个外部类
private class InnerClass(){ //定义一个内部类
//....
}
}
匿名内部类 必须继承一个父类或者实现一个接口
new 类名(){
...//内部类体
};
定义内部类的方式
1.一个在方法中定义的类(局部内部类)
2.一个定义在作用域内的类,这个作用域在方法的内部(成员内部类)
3.一个实现了接口的匿名类(匿名内部类)
4.一个匿名类,他扩展了非默认构造器的类
5.一个匿名类,执行字段初始化操作
6.一个匿名类,他通过实例初始化实现构造
9.2 内部类的分类
-
定义在外部类局部位置上(比如方法内):
1)局部内部类(有类名)
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名
- 可以直接访问外部类的所有成员,包括私有的。
- 不能添加访问修饰符,因为他的地位就是一个局部变量。局部变量是不能使用修饰符的。可以使用final修饰,因为局部变量也可以使用final。
- 作用域:仅仅在定义它的方法域或代码块中
- 局部内部类—访问---->外部类的成员 [访问方式:直接访问]
- 外部类----访问----->局部内部类的成员
访问的方式:创建对象,再访问(注意:必须在作用域内)
- 外部其它类----不能访问-----> 局部内部类(因为局部内部类地位是一个局部变量)
- 如果外部类和局部内部类的成员重名时,默认遵守就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
总结:1)局部内部类定义在方法中/代码块 2)作用域在方法体或者代码块中 3)本质仍是一个类
2)匿名内部类(没有类名,重点!!!!!)
注意事项和细节:
- 内部类的调用
//匿名内部类的使用方法1(推荐使用) new A(){//重写了A类中的方法cry() @override public void cry(){ System.out.println("hello") } }.cry();//{}包起来的其实就是对象 //匿名内部类调用方法2 A a = new A(){ @override public void cry(){ System.out.println("hello") } }; a.cry();
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,因为他的地位就是一个局部变量。
-
作用域:仅仅在定义它的方法或者代码块中
-
匿名内部类----访问---->外部类成员【访问方式:直接访问】
-
外部其他类----不能访问----->匿名内部类(因为 匿名内部类地位是一个局部变量)
-
如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员变量,则可以使用(外部类名.this.成员)去访问。
-
匿名内部类本质还是一个对象
-
定义在外部类的成员位置上
1)成员内部类(没有static修饰)
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰
1. 可以直接访问外部类的所有成员,包含私有的
2. 可以添加任意访问修饰符(public、protected、默认、private),因为他的地位就是一个 成员
3. 作用域和外部类的其他成员一样,为整个类体,在外部类的成员中创建内部类,再调用方法。
4. 成员内部类---访问---->外部类成员(比如:属性)【访问方式:直接访问】
5. 外部类---访问---->成员内部类【访问方式:创建对象,再访问】
6. 外部其他类---访问--->成员内部类【访问方式:创建一个getXXX方法返回该类对象用于测试方法调用对象的额方法】
7. 如果外部类和内部类的成员重名的时候,内部访问的话,默认遵循就近原则,如果想访问外部类成员,则可以使用 (外部类名.this.成员)去访问。
2)静态内部类(使用static修饰)
说明:静态内部类是定义在外部类的成员位置,并且有static修饰。
1. 可以直接访问内部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2. 可以添加任意访问修饰符(public、protected、默认、private),因为他的地位就是一个成员。
3. 作用域:同其他的成员,为整个类体
4. 静态内部类---访问--->外部类(比如:静态类)【访问方式:直接访问所有静态成员】
5. 外部类---访问--->静态内部类 【访问方式:创建对象,再访问】
6. 外部其他类---访问---> 静态内部类
7. 如果外部类和静态类成员重名时,静态内部类访问的时,默认遵循就近原则,如果访问的是外部类的成员,则可以访问外部类的成员,则可以使用(外部类名.成员)去访问。
10.异常(Exception)
10.1 Exception
Exception 位于 java.lang 包下,它是一个顶级接口,继承于Throwable 类,Exception类及其子类都是Throwable 的组成条件。
Throwable类是java语言中所有==错误**(errors)和异常(exceptions)的父类。只有继承于Throwable的类或者子类才能够被抛出,还有一种带有java中的@throw**==注解的类也可以抛出。除了RuntimeException和其子类,以及error和其子类,其他所有的异常都是checkedException,见下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPzajer2-1628148194897)(C:\Users\Chijianhua\AppData\Roaming\Typora\typora-user-images\image-20201204220632383.png)]
Exception有两种异常,一种是RuntimeException;一种是CheckedException,这两种异常都应该去捕获
java中常见的异常及其分类
RuntimeException
异常名称 | 异常描述 |
---|---|
ArrayIndexOutOfBoundsException | 数组越界异常 |
NullPointerException | 空指针异常 |
IllegalArgumentException | 非法参数异常 |
NegativeArraySizeException | 数组长度负异常 |
IllegalStateException | 非法状态异常 |
ClassCastException | 类型转换异常 |
SQLException | 操作数据库异常 |
IOException | 输入输出异常 |
UncheckedException
异常名称 | 异常描述 |
---|---|
NoSunchFieldException | 表示该类没有指定名称抛出来的异常 |
NoSuchMethodException | 表示该类没有指定方法抛出来的异常 |
IllegalAccessException | 不允许访问某个类的异常 |
ClassNotFoundException | 类没有找到抛出异常 |
10.2 捕捉异常 try、 finally、catch 快捷键(选中代码块) ctrl + alt + t
这三个关键字主要有以下的三种的组合方式:try…catch 、try…finally、try…catch…finally。
1.try…catch
try{
System.out.println("1");//try语句中用来存放的是可能发生异常的java语句
}catch(Exception e){
e.printStackTrace();//catch中用来激发被捕获的异常,最好写上处理异常的代码,用来打印日志
}
//当try语句中发生异常的时候将会转到catch语句中执行,执行完catch语句中的代码后,将会继续执行catch代码块后的其他语句
2.try…finally
表示一段代码不管执行情况如何,都会走finally中的代码块,finally优先于return执行
static void cacheException() throws Exception{
for(int i=0;i<5;i++){
System.out.println("enter:i="+i);
try{
System.out.println("execute:i="+i);
continue;
}finally{//不管如何必须会走完下面的代码块
System.out.println("leave: i="+i);
}
}
}
try-catch方式处理异常注意事项:
1、如果异常发生了,则异常发生在后面的代码不会执行,直接进入到catch块。
2、如果异常没有发生,则顺序执行try的代码块,不会进入到catch
3、如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用如下代码-finally{}
4、可以有多个catch语句,捕获不同的异常(进行不同的业务逻辑),要求父类异常在后,子类异常在前,比如(Exception在后,NullPointException在前),如果发生异常,只会匹配一个catch
5、可以进行try-finally配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉。应用场景,就是执行一段代码,不管是否发生异常,都必须执行某个业务逻辑。
如果程序员没有指定使用try-catch,那么默认使用throws
3.throws
throws关键字通常用被应用在声明方法时,用来指定方法可能跑出的异常。多个异常可使用逗号分隔
static void pop() throws IOException{}//定义方法并抛出异常
注意事项和使用细节:
1)对于编译异常,程序中必须处理,比如try-catch或者throws
2)对于运行异常,程序中如果没有处理,默认就是throws的方式处理
3)子类重写父类的方法时,对抛出异常存在异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出异常的子类型
4)在throws过程中,如果有方法try-catch,就相当于处理异常,就可以不必throws
4.throw 自定义异常
throw关键字通常运用在方法体中,并且抛出一个异常对象。通过throw关键字抛出异常后,如果想在上一级代码中来捕获并处理异常则必须要throws关键字在方法的生命中指明要抛出的异常;如果想捕捉throw抛出的异常则必须使用try…catch语句块。throw关键字通常是用户自定义异常
throws 和 throw的区别:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NHgszW0I-1628148194898)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20210322105430789.png)]
5.遵循原则
1.在当前方法声明中使用try…catch语句捕获异常
2.一个方法被覆盖时,覆盖他的方法必须抛出相同的异常或者异常的子类
3.如果父类抛出多个异常,则覆盖方法必须抛出那些异常的一个子集,不能抛出新的异常。
11.集合
集合又被称为是容器,集合与数组不一样的地方在于,数组的长度是固定的,集合的长度是可变的;数组用来存放基本数据类型的数据,而集合是用来存放对象的引用。常用的集合有List集合、Set集合和Map集合,其中List和Set继承了Collection接口。
11.1Colection接口
Collection是一个**顶层的接口,他主要是用来定义**集合的约定
Collection接口不能够直接被使用,但接口提供了添加元素、删除元素、管理数据的方法。如下所示:
方法 | 功能描述 |
---|---|
add(E e) | 将指定的对象添加到集合中 |
remove(Object o) | 将指定的对象从集合中移除 |
isEmpty() | 返回Boolean值,用于判断集合是否为空 |
itearor() | 返回在此Collection的元素上进行迭代的迭代器,用于遍历集合中的对象 |
size() | 返回int型值,获取集合中的个数 |
由于List和Set都继承了Collection接口,所以上述的方法在List和Set集合中也适用。
11.2 List集合
List集合包括了List接口及接口的所有实现类。List中元素是可以重复的,各元素的顺序就是对象插入的顺序,List集合不仅继承Collection集合的方法还定义了下面两种重要的方法:
方法 | 功能描述 |
---|---|
get(int index) | 获取指定索引位置的元素 |
set(int index,Object obj) | 将集合中指定的索引的位置的对象修改为指定的对象 |
11.2.1 ArrayList
ArrayList类实现了List接口的==可扩容数组(动态数组)==,允许保存所有元素,包括null,并可以根据索引的位置对集合进行快速的随机访问;缺点就是向指定索引删除或插入对象的速度慢且不是线程安全的容器。
List<E> list = new Arrylist<E>();
1.ArrayList可以实现所有选择的列表操作,允许所有的元素,包括null。ArrayList还提供了内部存储List的方法,它能够完全替代Vector,只有一点例外,ArrayList集合不是线程安全的容器
2.ArrayList有一个容器的概念,这个数组的容器就是List用来存储元素的容器
3.ArrayList不是线程安全的容器,如果多个线程中至少两个线程修改ArrayList的结构的话就会导致线程安全的问题,作为替代的条件可以使用线程安全的List,应使用**Collections.synchronizedList**。
List list = Collections.synchronizedList(new ArrayList(...))
4.ArrayList具有fail-fast快速失败机制,能够对ArrayList做出失败检测。当在迭代集合过程中该集合在结构上发生改变的时候,就有可能会发生fail-fast,即会抛出**ConcurrentModidicationException异常**。
11.2.2 Vector
Vector和ArrayList集合一样,都是基于数组实现的,只不过Vector是一个线程安全的容器,他的内部每个方法都是简单粗暴的上锁,避免多线程引起的安全性的问题,但是这种同步的方式开销比较大导致效率远远低于ArrayList。还有一个不同的是,在扩容上,ArrayList扩容后的数组长度会增加50%,而Vector的扩容会增加100%,如果在集合中使用较大的数据时,用Vector有一定优势。
注意:Vector也不是绝对的安全,不能出现**两个及两个以上的线程在同步时调用这些同步方法,不然会出现线程不安全**
11.2.3 LinkedList
LinkedList是一个双向链表,允许储存任何元素包括null。他主要的特性如下:
1.LinkedList所有的操作都可以表现为双向性的,索引到链表的操作将遍历从头到尾,是哪个距离近为遍历顺序。
2.注意这个实现也不是线程安全的,如果多个线程并发访问链表,并且至少其中一个的线程修改了链表的结构,那么这个链表必须进行外部加锁。或者使用:
List list = Collections.syschronizedList(new LinkedList(...))
Vector和ArraryList的比较?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N6Bo14iW-1628148194899)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20210405164310161.png)]
11.3 Set集合
Set集合中的对象不按特定的方式排序,只是简单的将对象加入到集合中,但是Set集合**不能包含**重复的对象。因为Set集合继承了Collection集合所以可以使用Collection的所有方法。它是由Set接口和Set接口的实现类组成。
Set接口常用的实现类有HashSet类和TreeSet类。
11.3.1 HashSet
HashSet是Set接口的实现类,由哈希表(实际上HashSet是HashMap的一个实例)。他不能保证集合的迭代顺序**(无序)**。这个类允许null元素。
注意:1.这个实现不是线程安全的。如果多线程并发访问HashSet,并且至少一个线程修改了Set,必须进行外部加锁。或者使用Collections.synchronizedSet()方法重写。
2.是个实现支持fail-fast机制
11.3.2 TreeSet
TreeSet类型不仅实现了Set接口,还实现java.util.Sorted.SortedSet接口,因此,TreeSet类实现的Set集合在遍历集合时按照自然排序递增排序,也可以按照指定比较器递增排序,即可以通过比较器对TreeSet类实现的Set集合中的对象进行排序。TreeSet类新增的方法如下所示:
方法 | 功能描述 |
---|---|
first() | 返回此Set中当前第一个(最低)元素 |
last() | 返回此Set中当前最后一个(最高)元素 |
comparator() | 返回对此Set中的元素进行排序的比较器。如果此Set使用的是自然序列,则返回null |
headSet(E toElement) | 返回一个新的Set集合,新集合包含toElement(不包含)之前的所有对象 |
subSet(E fromElement,E fromElement) | 返回一个新的set集合,包含fromElement(包含)对象与fromElement(不包含)对象之间的所有对象**(左闭右开)** |
tailSet(E fromElement) | 返回一个新的Set集合,新集合包括fromElement(包含)之后的所有对象 |
注意:
1.此实现为基本操作add,remove和contains提供了log(n)的时间成本。
2.这个实现不是线程安全的。如果多线程并发访问TreeSet,并且至少修改了Set,必须进行外部加锁。或者使用
SortedSet s = Collections.synchronizedSet(new TreeSet(...))
3.这个实现持有fail-fast机制。
11.3.3 LinkedHashSet
LinkedHashSet类继承于Set,LinkedHashSet的集成体系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GgiJjEzw-1628148194900)(C:\Users\Chijianhua\AppData\Roaming\Typora\typora-user-images\image-20201207121409907.png)]
LinkedHashSet是Set接口的Hash表和LinkedList的实现。这个实现不同于HashSet的是他维护着一个贯穿所有条目的双向链表。此链表定义了元素插入集合的顺序。注意:如果元素重新插入,则插入顺序不会受到影响。
1.LinkedHashSet有两个影响其结构的参数:初试容量和加载因子。他们的定义与HashSet完全相同。但是:对于LinkedHashSet,选择过高的初试容量值的开销要比HashSet小,因为LinkedHashSet的迭代次数不容易受到影响
2.注意:LinkedHashSet也不是线程安全的,如果多线程同时访问LinkedHashSet,必须外部加锁,或者通过使用
Collections.synchronizedSet
3.该类也支持fail-fast机制
HashSet和TreeSet分别如何实现去重的?
- Hashset的去重机制:hashCode()+equals(),底层先通过存入对象,进行运算得到一个hash值,通过hash值得到对应的索引,如果发现table索引所在的位置,没有数据,就直接存放,如果有数据,就进行equals比较[遍历比较],如果比较后,不相同,就加入,否则就不加入
- TreeSet的去重机制:如果你传入了一个Comparator匿名对象,就使用实现的compare去重,如果方法返回是0,就认为是相同的元素/数据,就不添加,如果你没传入一个Comparator匿名对象。则以你添加的对象实现Compareable接口的CompareTo去重。
11.4 PriorityQueue
PriorityQueue是AbstractQueue的实现类,优先级队列的元素根据自然排序或者在构造函数时期提供Comparator来排序,具体根据构造器判断。PriorityQueue不允许有null元素
1.队列的头在某种意义上是指定顺序的最后一个元素。队列查找操作poll,remove,peek和element访问队列头部元素。
2.队列优先级是无限制的,但是有内部capacity,用于控制用于在队列中存储元素的数组大小。
3.该类以迭代器实现了Collection、Iterator接口的所有可选方法。这个迭代器提供了**iterator()方法不能保证以任何特定的顺序遍历优先级队列的元素。如果你需要有序遍历,考虑使用Arrays.sort(pq.toArray())**.
4.注意这个不是线程安全的,多线程不应该并发访问PriorityQueue实例如果有某个线程修改了队列的话,使用**线程安全的类PriorityBlockingQueue**
11.5 Map
Map结合没有继承Collection接口,其提供的是key到value的映射。Map中不能包含相同的key,每一个key只能映射一个value。key还决定着存储对象在映射中的存储位置,但不时有key本身决定的,而是一种**“散列技术”**进行处理,产生一类散列码的整数值散列码通常用作一个偏移量,改偏移量对应分配给映射是内存区的起始位置,从而确定了存储对象在映射中的存储位置。Map集合包括Map接口以及Map接口的所有实现类。
Map接口实现类的特点:
- Map与Collection并列存在。用于保存具有映射关系的数据:key-Value
- Map中的key和value 可以是任何引用类型的数据,会封装到HashMap$Node对象中
- Map中的key不允许重复,原因和HashSet一样,前面分析过源码。
- Map中的value可以重复
- Map中的key可以为null,value 也可以为 null, 注意 key 为 null,只能有一个,value 为 null,可以多个。
- 常用String类作为Map的key
- key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
- Map存放数据的key-value示意图,一堆k-v是放在一个Node中的,有因为Node 实现了 Entry 接口,有些书上也说 一对k-v就是一个Entry
11.5.1 Map接口
Map中的key和value值是一一对应的关系,其常用的方法如下:
方法 | 功能描述 |
---|---|
put(K key,V value) | 向集合中添加指定的key和value的映射关系 |
containsKey(Object key) | 如果此映射包含指定的key的映射关系,则返回true |
containsValue(Object value) | 如果此映射将一个或多个key映射到指定值,则返回true |
get(Object key) | 如果存在指定的key对象,则返回对象对应的值,否则返回null |
keySet() | 返回该集合中所有key对象形成的Set集合 |
values | 返回该集合中所有值对象形成的Collection集合 |
遍历Map集合
Set<String> set = map.keySet();//获取Map集合中的key对象的集合
Iterator<String> it = set.iterator();// 构造一个迭代器用于进行集合遍历
while(it.hashNext()){//判断是否存在下一个元素,不是返回false(-1)
String str = (String)it.next();//取出下一个key返回给str
String name = (String)map.get(str);//根据返回的可以的到对应的值
}
11.5.2 Map接口的实现类
11.5.2.1 HashMap
HashMap是一个利用哈希表原理来存储元素的集合,并且允许空的key-value键值对**(必须保证键值对的唯一性)。HashMap能够根据哈希表对其内部的映射关系进行快速查找。不保证映射的顺序,特别是他不保证该顺序恒久不变,且HashMap不是线程安全的**,也就是说在多线程的环境下,可能会存在问题,而HashTable是线程安全的。
HashMap的实例有两个参数影响其性能:初始容量和加载因子。可以使用**Collections.synchronizedMap(new HashMap(…))**来构造一个线程安全的HashMap
11.5.2.2 TreeMap
TreeMap是一个基于NaviogableMap实现的红黑树。这个Map根据自然排序存储,或者通过Comparator进行定制排序(集合是有序的)。
1.TreeMap为containsKey,get,put和remove方法提供了log(n)的时间开销。
2.注意这个实现不是线程安全的。如果多线程并发访问TreeMap,并且至少一个线程修改了map,必须进行外部加锁。这通常通过在自然封装集合的某个对象上进行同步来实现,或者是使用
SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));
3.这个实现持有fail-fast机制。
11.5.2.3 LinkedHashMap
LinkedHashMap是Map接口的哈希表和链表的实现。这个实现与hashMap不同之处在于他维护了一贯穿其所有条目的双向链表。这个链表定义了遍历顺序,通常是插入map中的顺序。
1.它提供一个特殊的LinkedHashMap(int,float,boolean)构造器来创建LinkedHashMap,其遍历顺序是其最后一访问的顺序
2.可以重写**removeEldest(Map.Entry)**方法,以便在将新映射添加到map时强制删除过期映射的策略。
3.这个类提供了所有可选择的Map操作,并且允许null元素。由于维护链表的额外开销,性能可能会低于HashMap,有一条除外:遍历LinkedHashMap中的collection-views需要与map.size成正比,无论其容量如何,HashMap的迭代看起来花销更大,因为还要求时间与其容量成正比。
4.LinkedHashMap有两个因素影响了他的构成:初试容量和加载因子
5.注意这个实现不是线程安全的。如果多线程并发访问LinkedHashMap,并且至少一个线程修改了map,必须进行外部加锁。这通常要通过在自然封装集合的某个对象上进行同步实现
Map m = Collections.synchronizedMap(new LinkedHashMap(...));
6.这个实现持有fail-fast机制
11.5.2.4 IdentityHashMap
IdentityHashMap是比较小众的Map实现
1.这个类不是通常的Map实现!虽然这个类实现了Map接口,但是他故意违反了Map的约定,该约定要求在比较对象是使用equals方法,此类仅适用于需要引用相等语义的极少数情况
2.同HashMap,IdentityHashMap也是无序的,并且该类不是线程安全的,如果要使之线程安全可以调用**Collections.synchronizedMap(new IdentityHashMap(…))** 方法实现。
3.支持fail-fast机制
11.5.2.5 WeakHashMap
WeakHashMap类是基于哈希表的Map基础实现的,带有**弱键。weakHashMap中的entry当不在使用时还会自动移除。更准确的说,给定key的映射的存在将不会阻止key被垃圾收集器丢弃。**
1.基于map接口,是一种弱键相连,weakHashMap里面的键会自动回收
2.支持null值和null键
3.fail-fast机制
4.不允许重复
5.WeakHashMap经常用作缓存
11.5.2.6 HashTable
HashTable类实现了一个哈希表,能够将键映射到值。任何非空对象都可以用作键或值。
1.此实现类支持fail-fast机制
2.与新的集合实现不同,HashTable是线程安全的。如果不需要线程安全的容器,推荐使用HashMap,如果需要多线程高并发,推荐使用**ConcurrentHashMap**
11.6 集合实现类特征图
集合 | 排序 | 随机访问 | key-value存储 | 重复元素 | 空元素 | 线程安全 |
---|---|---|---|---|---|---|
ArrayList | Y | Y | N | Y | Y | N |
linkedList | Y | N | N | Y | Y | N |
HashSet | N | N | N | N | Y | N |
TreeSet | Y | N | N | N | N | N |
HashMap | N | Y | Y | N | Y | N |
TreeMap | Y | Y | Y | N | N | N |
Vector | Y | Y | N | Y | Y | Y |
HashTable | N | Y | Y | N | N | Y |
CocurrentHashMap | N | Y | Y | N | N | Y |
Stack | Y | N | N | Y | Y | Y |
CopyOnWriteArrayList | Y | Y | N | Y | Y | Y |
12.泛型
泛型其实就是一种参数化的集合,他限制了你添加进集合的类型。泛型的本质就是一种参数化类型,多态也可以看作是泛型的机制。
List<String> arryList = new ArrayList<String>();
arrayList.add(100);
//泛型的机制,语法如下:
类型<T> //其中T代表一个数据类型的名称
12.1 泛型的使用
12.1.1 用泛型表示类
泛型可以加到类上面来表示这个类的类型。
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
public class demo<T>{
//value 这个成员变量的类型为T,T的类型有外部指定
private T value;
public demo(T value){
this.value=value;
}
public T demo(){//泛型的达到getKey的返回值类型为T,T的数据类型由外部指定
return value;
}
public void setValue(T value){
this.value=value
}
}
一般数据类型名称使用T来表示,而容器的元素使用E来表示
泛型使用的注意事项和细节:
- interface List{}, public class HashSet{}…等等
说明:T,E只能是引用类型
List list = new ArrayList()
- 再给泛型指定具体类型后,可以传入该类型或者其子类类型
- 泛型的使用方式
List list = new ArraryList<>();//推荐
- 如果我们这样写 List list = new ArraryList();默认给他的 泛型就是[ E 就是 Object]
12.1.2 用泛型表示接口
泛型接口与泛型类的定义和使用基本相同。
//定义一个泛型接口
public interface Generator<T>{
public T next();
}
一般泛型接口常用于**生成器(generator)**中,生成器相当于对象工厂,是一种专门用来创建对象的类。
12.1.3 泛型的方法
可以使用泛型来表示方法
public class GenericMethods{
public <T> void f(T x){
System.out.println(x.getClass().getName());
}
}
12.2 泛型的高级用法
12.2.1 限制泛型可用类型
默认使用任何类型来实例化一个泛型的对象,但是java中也对泛型类的实例做了限制。
class 类名称<T extends anyClass>
其中,anyClass是**某个接口或类。使用泛型之后,泛型类的类型就必须是实现或者继承了anyClass这个接口或者类。必须使用extends关键字**
12.2.2 泛型的通配符
List是泛型类,为了表示各种泛型List的父类,可以使用类型通配符,类型通配符使用**问号(?)**表示,他的元素可以匹配任何类型。
public static void main(String[] args){
List<String> name = new ArrayList<String>();
List<Integer> age = new Arraylist<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("lisi");
age.sdd(23);
number.add(0326);
generic(name);
generic(age);
generic(number);
}
public static void generic(List<?> data){
System.out.println(data.get(0));
}
上界通配符:<? extends A> 该通配符可以接受 A 或者 A 的所有子类型。
下界通配符:<? super A> 该通配符可以接受A 或者 A 的所有超类。
12.3 自定义泛型
12.3.1 使用细节
- 普通成员可以使用泛型(属性、方法)
- 使用泛型的数组,不能初始化
- 静态方法中不能使用类的泛型
- 泛型的类型,是在创建对象的时候确定的(因为创建对象时,需要指定确定类型)
- 如果在创建对象时,没有指定类型,默认为Object
12.3.2 自定义泛型接口
使用细节:
- 接口中,静态成员变量也不能使用泛型(这个和泛型类规定是一样的)
- 泛型接口的类型,在继承接口或者实现接口时确定
- 没有指定类型,默认为Object
13.IO
流是一组有序的数据序列,根据操作的类型,可分为输入流和输出流两种
13.1 输出流和输入流的区别
1.输入流是**得到数据,输出流是输出**数据。流就像是管道一样,在程序和文件之间,输入输出的方向是针对程序而言,向程序中读入东西就是输入流,从程序中向外读出东西就是输出流。
2.stream结尾的都是字节流,reader和writer结尾的都是字符流。
3.在字节(按字节读写)输出数据主要是使用OutputStream完成,输入使用的是InputStream.
在字符(按字符读写)流中输出主要的是使用Writer类完成的,输入流主要是Reader类完成。
4.在读写文件需要对内容进行处理,比如比较特定的字符,处理某一行数据的时候一般选择字符流。
只是读写文件,和文件的内容无关的,一般选择字节流。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IKxyQpln-1628148194901)(C:\Users\Chijianhua\AppData\Roaming\Typora\typora-user-images\image-20201210102120108.png)]
13.2 IO流的思维导图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QHtvHZmt-1628148194902)(C:\Users\Chijianhua\AppData\Roaming\Typora\typora-user-images\image-20201210102246424.png)]
详细请看思维导图(IO流.xmind)
I/O还可以根据操进行划分:主要分为
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nGC1BJjH-1628148194903)(C:\Users\Chijianhua\AppData\Roaming\Typora\typora-user-images\image-20201210102745330.png)]
详细看(IO操作对象)
13.3 File类
File类是对文件系统中文件以及文件夹进行操作的类。文件创建操作如下,主要涉及 文件创建、删除文件、获取文件描述等。
public class file {
public static void main(String[] args) {
File f = new File("D:\\file.text");//填写要创建文件的路径
try {
f.createNewFile();//创建一个文件
//File类中的两个常量
//路径分隔符(与系统有关的)<windows里面是 ; Linux里面是 :>
System.out.println(File.pathSeparator);
//与系统有关的路径名称分隔符<windows里面是 \ linux里面是 />
System.out.println(File.separator);
//删除文件
// File f = new File(fileName);
if (f.exists()) {
f.delete();
} else {
System.out.println("文件名不存在");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
也可以对文件夹进行操作
class FileDemo{
public static void main(String[] args){
String filename = "D"+File.separator+"filepackage";
File f = new File(fileName);
f.mkdir();
//列出所有文件
String[] str = f.list();
for(int i =0;i<str.length;i++){
System.out.println(str[i]);
}
//使用file.listFiles();列出所有文件,包含隐藏文件
//使用file.isDirectory();判断指定路径是都是目录
}
}
13.3.1 创建文件
File(String directoryPath);
File(String directoryPath,String filename);
File(File dirObj,String filename);
其中,directoryPath是文件路径名,filename是文件名,dirObj是file对象。例如:
File file = new File("D:\\java\\file1.text");//双\\表示转义
File file2 = new File("D:\\java","file2.text")//父路径、子路径--可以适用于多个文件的形式
File parent = new File("D:\\java")
File file3= new File(parent,"file3.text")//File类的父路径、子路径
13.3.2 对File类进行总结
方法 | 方法描述 |
---|---|
public String getName() | 返回此路径名表示的文件或目录名称 |
public String getParent() | 返回此路劲名的父路径名的字符串 |
public String getPath() | 返回此路径名转换成一个路径的字符串 |
public boolean isFile() | 测试此路径名表示的文件是否是一个标准文件 |
public boolean equals(Object obj) | 测试此路径名与给定的对象是否相等 |
public String[] list() | 返回此路径名所表示的目录文件和目录的名称所组成字符创数组 |
public boolean mkdir() | 创建此路径名指定的目录 |
public String getAbsolutePath() | 返回路径名的绝对路径名字符串 |
public boolean exists() | 测试此路径名表示的文件和目录是否存在 |
13.4 基础IO类和相关方法
虽然IO类有很多,但是最基本的是四个抽象类,InputStream、OutputStream、Reader、Writer.最基本的方法也就是**read()和writer()方法,其他流都是上面四类流的子类,方法也是通过这两类方法衍生而成的。而大部分的IO源码都是native**标志的,也就是说源码都是C/C++写的。
13.4.1 InputStream
InputStream是一个定义了java流式字节输入模式的抽象类。该类方法在出错条件下引发一个IOException异常。他的方法定义如下:
方法 | 方法介绍 |
---|---|
pubilc int available() | 返回可读的字节数量 |
public int read(byte b[],int off,int len) | 把从第off位置读取len长度字节的数据放到byte数组中 |
public abstract int read() | 读取数据 |
public long skip(long n) | 跳过指定个数的字节 |
public void close() | 关闭流,释放资源 |
public synchronized void reset() | 重置读取位置为上次mark标记的位置 |
public boolean markSupported() | 如果调用的流支持mark()/reset()就返回true |
13.4.2 OutputStream
OutputStream是定义了流式==字节流==输出模式的抽象类。改类的所有方法返回一个void值并且在出错情况引发一个IOException异常。它的主要方法定义如下:
方法 | 描述 |
---|---|
void write(int b) | 向输出流写入单个字节 |
void write(byte buffer[]) | 向一个输出流写入一个完整的字节数组 |
void write(byte buffer[],int offset, int numBytes) | 写入数组buffer以buffer[offset] |
void flush() | 刷新缓冲区 |
void close() | 关闭输出流。关闭后的写操作会产生IOException异常 |
13.5 Reader类
Reader 是Java 定义的流式字符输入模式的抽象。类中的方法在出错时引发IOException异常
方法 | 描述 |
---|---|
int read() | 如果调用输入流的下一个字符可读则返回一个整型。遇到文件尾时返回-1。 |
int read(char buffer[]) | 从缓冲区中读取字符,返回时机成功读取的字符数。遇到文件尾时返回-1。 |
abstract int read(char buffer[],int offset, int numChars) | 试图读取buffer中从buffer[offset]开始的numChars个字符,返回时机成功读取的字符数。遇到文件尾返回-1. |
boolean ready() | 如果下一个输入请求不等待则返回true,否则返回false。 |
long skip(long numChars) | 跳过numChars个输入字符,返回跳过的字符数 |
boolean markSupported() | 判断当前的流是否支持标记流 |
void reset() | 重置读取位置为上次mark标记的位置 |
void mark(int numChars) | 在输入流的当前位置设置一个标记。该输入流在numChars个字符被读取之前有效 |
abstract void close() | 关闭输入源。进一步读取将会产生IOException异常 |
**Filereader文件字符输入流:**把文件转换为字符流读入
CharArrayReader字符数组输入流: 是一个把字符数组作为源的输入流的实现
**BufferedReader:**BufferedReader类从字符输入流中读取文本并缓冲字符,以便有效地读取字符,数组也行
**PushbackReader:**PushbackReader类允许一个或多个字符被送回输入流
**PipedReader管道输入流:**主要用途也是在线程间通信,不过这可以用来传输字符
13.6 Writer 类
Writer 是定义流式==字符==输出的抽象类。所有该类的方法都返回一个void值并在出错条件下引发IOException异常
方法 | 描述 |
---|---|
void write(char buffer[]) | 向一个输出流写一个完整的字符串数组 |
abstract void write(char buffer[],int offset,int numChars) | 向调用的输出流写入数组buffer以buffer[offset]为起点的numChars个字符区域的内容 |
abstract void close() | 关闭输出流。关闭后的写操作会产生IOException异常 |
abstract void flush() | 刷新缓冲区 |
Writer append(CharSequence csq) | 追加一个字符序列 |
Writer append(CharSequence csq,int start,int end) | 追加写入一个字符序列的一部分,从start位置开始,end位置结束 |
Writer append(char c) | 追加一个16位的字符 |
**FilterWriter字符输出流:**FileWriter创建一个可以写文件的Writer类
CharArrayWriter字符数组输出流:CharArrayWriter实现了一数组作为目标的输出流
**BufferedWriter 缓冲区输出流:BufferedWriter是一个增加了flush()**方法的Writer。flush()方法可以用来确保数据缓冲器确实被写到实际的输出流
PrintWriter:PrintWriter本质上是PrintStream的字符形式的版本
PipedWriter管道输出流:主要用途也是在线程的通讯,不过这个可以用来传输字符
13.7 InputStream及其子类
FileInputStream文件输入流:FileinputStream类创建一个能从文件读取字节的InputStream类
ByteArrayInputStream字符数组输入流:把内存中的一个缓冲区作为InputStream使用
**PipedInputStream管道输入流:**实现了pipe管道的概念,主要是在线程中使用
SequenceInputStream顺序输入流:把多个InputStream合并为一个InputStream
**FileterOutputStream过滤输入流:**其他输入流的包装
ObjectInputStream反序列化输入流:将之前使用ObjectOutputStream序列化的原始数据恢复为对象,以流的方式读取对象
**DataInputStream:**数据输入流允许引用程序已与机器无关的方式从底层输入流中读取基本Java数据类型
PushbacklInputStream退回输入流:缓冲的一个新颖的用法是实现**推回(pushback)** Pushback用于输入流允许字节读取然后返回到流
13.8 OutputStream及其子类
**FileOutputStream文件输出流:**该类实现了一个输出流,其数据写入文件
**ByteArrayOutputStream字节数组输出流:**该类实现了一个输出流,其数据被写入由byte数组充当的缓冲区,缓冲区会随着数据的不断写入而自动增长。
PipedOutputStream管道输出流:管道的输出流是管道的发送端。
ObjectOutputStream基本类型输出流:该类将**实现了**序列化的对象序列化写入到指定的地方
**FilterOouputStream过滤输出流:**其他输出流的包装
**PrintStream打印流:**通过PrintStream可以将文字打印到文件或者网络中去。
**DataOutputStream:**将数据输出流允许应用程序以与机器无关方式向底层输出流中写入基本java数据类型
Java的输入输出的流式接口为复杂而繁重的任务提供了一个简介的抽象,过滤器类的组合可以允许你动态建立客户端流是接口来配合数据传输的要求。
13.9 对象流 - ObjectInputStream和ObjectOutputStream
1、序列化和反序列化
- 序列化就是在保存数据时,保存**数据的值和数据类型**
- 反序列化就是在恢复数据时,恢复**数据的值和数据类型**
- 需要让某个对象支持序列化机制,则需要让其类可以序列化的,为了让某个类是可以序列化的,该类必须实现如下的两个接口
- Serializable //这是一个标记接口,没有方法
- Externalizable //该接口需要方法实现,因此我们一般实现上面的Serializable接口
2、注意事项和使用细节
- 读取顺序要一致
- 要求序列化或反序列化对象,需要实现Serializable
- 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
- 序列化对象时,默认将里面所有的属性都进行序列化,但除了static或transient修饰的成员
- 序列化对象时,要求里面属性的类型也需要实现序列化接口
- 序列化具有可继承性,也就是如果某类已经实现序列化,则他的所有子类也已经默认实现了序列化
3、标准输出输入流
类型 | 默认设备 | |
---|---|---|
System.in 标准输入 | InputStream | 键盘 |
System.out 标准输出 | PrintStream | 显示器 |
14. 反射
反射是Java中一个非常重要同时也是一个高级特性,基本上Spring等一些系列的框架都是基于反射的思想写成的。
java反射机制在程序运行过程中,对于任何一个类,都能够知道它的所有属性和方法;对于任意 一个对象,都能够知道调用他的任意属性和方法,这种动态获取信息以及调用对象方法的功能称为java语言的反射机制。
反射机制主要提供了以下这几个功能
1.在运行时判断任意一个对象所属的类
2.在运行是构造任意一个类的对象
3.在运行时判断任意一个类的所有的成员变量和方法
4.在运行时调用任意一个对象的方法
这么一看,反射就像是一个掌握全局的角色,不管你程序怎么运行,我都能够知道你这个类有那些属性和方法,你这个对象是由谁调用。很吊!!!开上帝视角
在java中,使用==java.lang.reflect== 包实现了反射机制。Java.lang.reflect所设计的类如下:
14.1 Class类
在java中,你没定义一个java class 实体都会产生一个Class对象。也就是说,当我们编写一个类,完成编译后,在生成的**.class文件中,就会产生一个Class对象,这个Class对象用于表示这个类的类型信息。Class中没有公共的构造器**,也就是说Class对象不能被实例化。
toString()
public String toString(){
return(is Interface()?"interface":(isPrimitive()?"":"class"))+getName();
}
toString方法能够将对象转换成字符串,toString()首先会判断Class类型是否是接口类型,也就是说,普通类和接口类都能够用Class对象表示,然后在判断是否是基本数据类型,这个判断的都是基本数据类型和包装类,还有void类型。
所有类型的包装类如下:
java.lang.Boolean:代表boolean数据类型的包装类
java.lang.Character:代表char数据类型的包装类
java.lang.Byte:代表byte数据类型的包装类
java.lang.Short:代表short数据类型的包装类
java.lang.Integer:代表int数据类型的包装类
java.lang.Long:代表long数据类型的包装类
java.lang.Float:代表float数据类型的包装类
java.lang.Double:代表double数据类型的包装类
java.lang.Void:代表void数据类型的包装类
**getName()**方法,这个方法返回类型的全限定名称。
如果是引用数据类型,比如String.class,getName()->java.lang.String
如果是基本数据类型,byte.class.getName()-> byte
如果是数组类型,(new Object[3]).getClass().getName()->java.lang,Object
toGenericString():这个方法会返回类的权限名称,而且包括类的修饰符和类型参数信息。
**forName()😗*根据类型获得一个Class对象的引用,这个方法会使类对象进行初始化
例如:**Class t = Class.forName(“java.lang.Thread”)**就能够初始化一个Thread线程对象,在java中,一共有三种获取实例的方式:
1.Class.forName(java.lang.Thread)
2.Thread.class
3.Thread.getClass()
newInstance():创建一个类的实例,代表着这类的对象。上面forName()方法对类进行初始化,newInstance方法对类进行实例化
getClassLoader():获取类加载器对象
getTypeParameters():按照声明的顺序获取对象的参数类型信息
**getPackage()😗*返回类的包
getInterfaces():获得当前类实现的类和接口,可能是有多个,所以返回的是Class数组。
**Cast:**把传递的类的对象转换成代表其子类的对象
**getClasses():**返回一个数组,数组中的包含该类型中所有的公共类和接口类的对象
getDeclaredClasses():返回一个数组,数组中包含该类中的所有类和接口类
**getSimpleName():**获得类的名字
**getFields():**获得所有公有的属性对象
**getFiled(String name)😗*获得某个属性对象
**getAnnotation(Class annotationClass)😗*返回该类中与参数类型匹配的公有注解对象
**getAnnotationgs():**返回该类的所有公有注解对象
getDeclareedAnnotations():返回该类的所有的注解对象
**getConstructors()😗*获取该类的所有公有构造方法
**getConstructor(Class…<?> parameterTypes)😗*获得该类中与参数类型匹配的构造方法
getDeclaredConstructors():获得该类的所有构造方法
**getMethod(String name,Class<?>parameterTypes)😗*获取该类某个公有的方法
**getMethods()😗*获取该类的公有的方法
**getDlcaredMethod(String name,Class…<?>parameterTypes)😗*获取该类的某个方法
**getDeclaredMethods()😗*获取该类的所有的方法
14.2 File类
Field类提供类或接口中单独的字段信息,以及对单独字段的动态反问。更多的方法参考API
几个常用的方法:
**equals(Object obj)😗*属性与obj相等则返回true
**get(Object obj)😗*获取obj中对应的属性值
**set(Object obj,Object value)😗*设置obj中对应属性值
14.3Method类
**invoke(Object obj,Object…args)😗*传递object对象及参数调用该对象对应的方法
14.4 ClassLoader类
反射中,还有一个非常重要的类就是ClassLoader类,类装载器是用来把类(class)装载进JVM的。ClassLoader使用的是双亲委托模型来搜索加载类的,这个模型也就是双亲委派模型。ClassLoader的类继承图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tFwWBr5I-1628148194903)(C:\Users\Chijianhua\AppData\Roaming\Typora\typora-user-images\image-20201212213545201.png)]
15.网络通信
15.1 局域网和因特网
局域网:(Local Area Net)就是通过一定形式连接起来的计算机
广特网:(Wide Area Net)由LAN延伸到更广的范围
因特网:(Internet)就是由无数的LAN和WAN组成的
要实现两台计算机之间的通信,就必须要用一个网络线路连接两台计算机,就必须要规定网络协议确保两台计算机能够准确无误的通信。
15.2 网络协议
网络协议规定了计算机之间连接的物理、机械(网络与网卡的连接规定)、电气(有效电平的范围)的特征以及计算机之间的相互寻址规则、数据发送冲突的解决、长的数据类型如何分段传输与接收等。
15.2.1 IP协议
IP(internet protocol),它是一种网络协议。Internet网络采用的是TCP/IP协议。在Internet网络存在着数以万计的电脑,Internet为他们分配网络地址代表自己,这就是IP地址。其中,IP 地址可用4个字节(一个字节八位)也就是32位的二进制数表示称为IPv4 例如127.0.0.1。也可以用16个字节表示IP地址,称为IPv6.
TCP/IP模式是一种层次结构,共分为4层,分别为应用层、传输层、互联网层和网络层。相互独立各自实现特定的功能。
15.2.2 TCP与UDP协议
在TCP/UDP协议栈中,有两个高级协议即==传输控制协议==(Transmission Control Protocol,TCP)与**用户数据报协议**(User Datagram Protocol, UDP)
TCP 是一种以固接线为基础的协议,他提供两台计算机之间可靠的数据传送。TCP有**三次握手**确保从一端数据发送至连接另一端的时候数据能够准确的送达,而抵达的数据排序和送出的时候相同。因此TCP适用于要求比较高的场合,反应比较慢,比如打电话、HTTP、FTP和Telnet
UDP是无连接通信不保证数据传输的准确性但是能够向若干个目标发送数据,接收发自若干个源的数据。UDP 是以独立发送数据包的方式进行。这种方式就像是邮递员送信可以将很多的信送到同一个人的手中,而每一封信又是独立的,每一封信送达的顺序并不重要收信人接收信件的时候也不能保证与发出的时候是一样的。UDP适用于一些对数据要求不高的场合,如网络聊天室、在线影片等。
TCP协议在认证上存在额外耗费,可能使传输的速度减慢,而 UDP协议适用于对传输速率要求很高的网站,即使有一小部分的数据丢失或者顺序不相同也不会严重危害该项通信。
15.2.3端口和套接字
一般而言,一台计算机只有一个单一的接口连接到网络的物理连接,所有的数据都通过此连接对内、对外送达特定的计算机,这就是端口(Port). 端口被规定在一个0~65535之间的整数。通常,0–1023的接口被定义一些知名的网络服务和应用,例如:HTTP为80 端口,FTP为21 端口。普通用户一般定义在1023~65535之间避免和一些服务端口冲突。
网络程序中的套接字(Socket)用于将网络程序和接口连接起来。套接字是一些遐想的连接装置就像是插插头的设备“插座”用于将连接电器与电线一样。java将套接字抽象化为类,程序设计中只需要创建Socket类对象,即可使用套接字。
15.3 TCP程序设计基础
TCP网络程序设计是指利用Socket类编写通讯程序,利用TCP协议进行通信的两个应用程序是由主次之分的,一个称为服务器程序一个称为客户机程序。
15.3.1 InetAddress类
java.net 中的InetAddress类是与IP地址有关的类,利用该类可以获取IP地址和主机地址信息,常用的方法有:
方法 | 返回值 | 说明 |
---|---|---|
getByName(String host) | InetAddress | 获取与host相对应的InetAddress对象 |
getHostAddress() | String | 获取InetAddress对象所含的ip地址 |
getHostName() | String | 获取此IP地址的主机名 |
getLocalHost() | InetAddress | 返回本地主机的InetAddress对象 |
15.3.2 ServerSocket类
java.net 包中的ServerSocket类用于表示服务器套接字,其主要的功能就是等待来自网络上的“请求”,他可以通过指定的端口来等待连接的套接字。服务器套接字一次可以与一个套接字连接,如果多台用户同时提出请求,服务器的套接字将会将请求的客户机存入到队列中,然后取出一个套接字与服务器套接字相连。若请求连接数大于最大容量(默认大小50),则多出的请求会被拒绝。
ServerSocket类的构造方法会抛出IOException异常,
1.ServerSocket():创建非绑定服务器套接字
2.ServerSocket(int port):创建绑定到特定的端口中的套接字
3.ServerSocket(int Port,int backlog):利用指定的backlog创建服务器套接字并将其绑定到指定的本地端口
4.ServerSocket(int port,int backlog,InetAddress bindAdress):使用特定的端口、侦听backlog和要绑定到的本地IP地址创建服务器。这种情况使用与计算机上有多块网卡和多个IP地址的情况,用于可以明确规定ServerSocket在那块网卡或IP地址上等待客户的连接请求
ServerSocket类的常用方法如下:
方法 | 返回值 | 说明 |
---|---|---|
accept() | Socket | 等待用户级的连接,若连接,则创建一个套接字 |
isBound() | boolean | 判断ServerSocket的绑定状态 |
getInetAddress() | InetAddress | 返回此服务器套接字的本地地址 |
isClosed() | boolean | 判断此服务器的套接字是否是关闭状态 |
close() | void | 关闭服务器套接字 |
bind(SocketAddress endpoint) | void | 将ServerSocket绑定到特定的地址(IP地址和端口号) |
getInetAddress() | int | 返回此服务器套接字等待的端口号 |
调用ServerSocket类的accept()方法会返回一个和客户端Socket对象相连的Socket对象,服务器端的Socket对象 使用的getOutputStream()方法获得输出流将指向客户端Socket对象使用**getInpotStream()**方法获得的那个输入流。
accept()方法会堵塞线程的继续执行,直到接收到客户的呼叫。
15.4 UDP程序设计基础
用户数据报协议(UDP)是网络信息传输的另一种形式。基于UDP的通信和基于TCP通信是不一样的,基于UDP的信息传递更快,但是不提供可靠的保证,所以使用UDP传送数据时,用户无法得知数据能否正确的传输到大指定的主机,也不能确定到达目的地的顺序是否和发送的时候是一样的。
基于UDP通信的基本模式如下:
1.将数据打包(称为数据包),然后将数据发往目的地,
2.接受别人发来的数据包,然后查看数据包。
UDP程序的步骤:
发送数据:
1.使用**DatagramSocket()**创建一个数据包的套接字
2.使用**DatagramPacket(byte[] buf,int offset,int length, InetAddress address,int port)**创建要发送的数据包
3.使用DatagramSocket类的sent()方法发送数据包
接受数据包:
1.使用**DataSocket(int port)**创建数据包套接字,绑定到指定的端口
2.使用**DatagramPacket(byte[] buf,int length)**创建字节数组用来接收来的数据包
3.使用DatagramPacket类的receive()方法接收UDP包
在正常的情况下,receive()方法会堵塞线程进行,一直等到接收到数据传来,receive()将接收到的数据返回。
15.4.1 DatagramPacket类
java.net 包下的DatagramPacket类用来表示数据包。DatagramPacket类的构造函数有:
1.**DatagramPacket(byte[] buf,int length):**创建DatagramPacket对象,指定了数据包内存空间和大小。
2.**DatagramPacket(byte[] buf, int length,InetAddress address,int port):**该构造函数创建发送数据的DatagramPacket对象,不仅指定了数据包的内存空间和大小还指定了数据包的目的地和端口。在发送数据的时候必须指定接收方Socket地址和端口号
15.4.2DatagramSocket类
java.net包中DatagramSocket类用于表示发送和接受数据包的套接字。该类的构造函数有:
1.DatagramSocket():创建DatagramSocket对象,构造数据报套接字并将其绑定到本地主机的任何可用的端口
2.DatagramSocket(int port):创建DatagramSocket对象,构造数据报套接字并将其绑定到本地主机上的指定端口
3.DatagramSocket(int port,InetAddress address):创建DatagramSocket对象,创建数据报套接字,并将其绑定到指定的本地地址,适用于多个网卡和多个IP地址的情况。
在接收程序时,必须指定一个端口号,不要让系统随机产生,此时就可以用第二种构造函数。就好比你要给某个朋友写信你不知道他的地址是不行的。
在发送程序时,通常使用第一种构造函数,不指定端口号,这样 系统就会为我们分配一个端口号,就像我们去寄信一样不需要去指定的邮局去寄。
16.关于null的集中处理方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dNk0V2P3-1628148194904)(C:\Users\Chijianhua\AppData\Roaming\Typora\typora-user-images\image-20201213151125517.png)]
具体的null的思维导图见
16.1 大小写敏感
首先,null是java中的**关键字**,像是public、static、final。它是带小写敏感的,你不能将null写成Null或NULL,编辑器将不能事变他们从而报错。
16.2 null是任何引用类型的初始值
null是所有引用类型的默认值,java中的任何引用变量都将null作为默认值,也就是说所有Object类下的引用类型默认值都是null。这对所有的引用变量都是适用。就像是基本数据类型的默认值一样,例如:int的默认值是0,Boolean的默认是false
类型 | 初始值 |
---|---|
boolean | false |
char | /u0000 |
byte | (byte)0 |
short | (short)0 |
int | 0 |
long | 0L |
float | 0.0L |
double | 0.0d |
16.3 null只是一种特殊的值
null**既不是对象也不是一种数据类型**,他仅是一种特殊的值,你可以将它赋值给任何类型,你可以将null转换为任何类型。
public static void main(String[] args){
String str = null;
Integer itr = null;
Double dou = null;
Integer integer = (Integer)null;
String string =(String)null;
System.out.println("integer="+integer);
System.out.prinyln("string="+string);
}
你可以看它在编译和运行期间内,将null转换成任何的引用类型都是可行的,并且不会抛出空指针异常
16.3.1 null值只赋值给引用变量,不能赋值给基本类型变量。
持有null的包装类在进行自动拆箱的时候,不能完成转换,会抛出空指针异常,并且null也不能和基本数据类型进行对比。
public static void main(String[] args){
int i=0;
Integer itr= null;
System.out.println(itr==i);
}//不可比较
使用了带有null值的引用类型变量,instanceof操作会返回false
public ststic void main(String[] args){
Integer isNull=null;
//instanceof=isInstance方法
if(isNull instanceof Integer){
System.out.println("isNull is instanceof Integer");
}else{
System.out.println("isNull is not instanceof Integer");
}
}
这是instanceof操作符一个很重要的特性,使得对类型的强制转换检查很有用
静态变量为null调用静态方法不会抛出NullPointException.因为静态方法使用了静态绑定。
16.4 使用Null-Safe方法
你应该使用null-safe安全的方法,java类库中有很多工具类都提供了静态方法,例如基本数据类型的包装类,Integer,Double等。
public class NullSafeMethod{
private static String number;
public static void mian(String[] args){
String s = String.valueOf(number);
String string= number.toString();
System.out.println("s="+s);
System.out.println("string="+string);
}
}
null没有赋值,所以默认为null,使用String.value(number)静态方法没有抛出空指针异常,但是使用toString()却抛出空指针异常。所以尽量使用对象的静态方法
16.4 null判断
你可以使用**或者!=** 进行比较null值,但是不能使用其他的算法或者逻辑操作,例如小于或者大于。跟SQL不一样,在java中nullnull将返回true。
public void static main(String[] args){
private static String str1;
private static String str2;
public static void main(String[] args){
System.out.println("str1==str2?"+str1==str2);
System.out.println(null==null);
}
}
17.SQL
MySql初级基础查看思维导图SQL
18.枚举
18.1 枚举的特点
1、构造器私有化
2、本类内部创建一组对象
3、对外暴露对象(通过为对象添加public final static修饰符)
4、可以提供get方法,但是不要提供 set方法
18.2 如何实现枚举类
1、用enum来实现枚举类(使用关键字enum 代替 class)
2、public static final Season SPRING = new Season(“春天”,“温暖”) 直接使用 SPEING(“春天”,“温暖”) ,解读:常量名(实参列表)、
3、如果有多个常量(对象),使用“ ,”(逗号隔开)即可最后一个用分号“;”。
4、如果使用enum 来实现枚举,要求将定义常量对象,写在最前面。
5、如果我们使用的是无参构造器,创建常量对象,则可以省略 ()
6、枚举不可以实现继承但是可以实现接口。