javaSE
历史
Java语言开发者 \ Java之父: 詹姆士.高士林(Gosling)
Sun Microsystem(太阳微系统)
被收购,Oracle公司(甲骨文): Oracle
Java版本:
- JavaSE: Java Standard Edition 标准版
- JavaEE: Java Enterprise Editition 企业版 并发技术
- JavaME: Java Mobile Edition 移动版
JDK,JRE,JVM
- JRE:Java Runtime Environment (Java运行时) :就是java运行的环境
- JDK: Java Development Kit (Java开发工具包):
里面有java开发所需的编译器,运行器,函数库等……
JDK中包含JRE。 - JVM : Java Virtual Machine (Java虚拟机)
是一个软件程序,运行字节码文件的,并且不能单独
好处,可以实现跨平台运行
Java语言特性
- 简单性:Java舍弃了C++中难以掌握并不安全的功能,如:指针、多继承等,Java语言底层是C++实现的
- 面向对象:Java和C++一样,是一种面向对象编程语言
- 安全性:如:运行时堆栈溢出,强制类型检查
- 健壮性:Java语言在运行过程中产生的垃圾会自动回收,简称GC机制
- 可移植性:Java程序编译一次,不做任何修改时到处运行,也就是跨平台
术语
OP: 面向过程
OOA: 面向对象分析
OOD: 面向对象设计
OOP: 面向对象编程
team: 团队
leader: 领导
PM:
1.项目经理: 有全盘项目管理,开发能力,沟通能力强
2.产品经理: 产品经理对软件产品负责,一般是在开发前,他负责与客户沟通。了解和收集客户需求,就开始画图(设计产品原型) UI
面向对象 与 面向过程的区别
什么是面向过程
概念: 做事情有一定的顺序或因果关系,那么这种程序设计为面向过程的程序设计。
优点:简单,开发时间短,无需团队直接就可以开发。无需任何软件开发模型。
缺点:耦合度高。什么是面向对象
概念:把生活中每个实物看做一个对象,然后把各个对象联系起来,经过一系列动作,来整体做一件事。
优点: 把生活中的实物理解对对象,对象之间有联系,开发就去找对象,耦合度低。
适合大型项目,彼此有联系但互不影响。
缺点: 开发周期长,文档多。看代码
1
2
3
4
5
6
7
8
9
10
11public class Test1 {
public static void main(String[] args){
int n=10;
print(n);
System.out.println(n);
}
public static void print(int n) {
n++;
}
}
//最后输出10
包
- 概念:类似于windows中的文件夹
- 作用是: 避免同名文件的冲突
- 关键字:package
- 语法: package 包名;
- 取名的规范:
package a;
或者 按照域名的反写com.bjpowernode.it.oa;
(域名.部门名.项目名;)
变量
- 概念: 变量是一个盒子,盒子的大小由数据类型来决定,盒子放入的是数据,数据的内容是可以变化的。
- 分类:
- 局部变量:在函数中定义的变量,有效范围在整个函数中有效。
- 全局变量:在类中定义的变量,有效范围在整个类中有效。
- 有作用域(使用范围)
1
2
3
4{ //这一对大括号就是一个作用域,变量a在此大括号中有效.
int a=1;
} //这一对大括号是叫代码块.
System.out.println(a);//此处不能输出
看代码
1 | public class Test1 { |
标识符
- 概念:是程序员取的合法的一个名字,这个名字可以用在类,函数,变量处等等。
- 命名规则:
- 标识符必须以字母开头
- 标识符组成:字母,数字,下划线,$
- 标识符不能以数字开头,可以组合4大部分。
- 标识符可以取中文吗? 可以的,中文也是字符,和字母类似,直接使用。但是项目中不用
- 标识符用在类处,要求类名首字母大写。
- 不能用关键字
- 标识符规范
- 驼峰、下划线命名
- 望文生义
Java代码执行机制
分为两个阶段:
- 编译阶段
- 编写Java源代码,保存为.java的文件
- 打开命令提示符—>写入一个javac 源文件名
- 编译完成后,会形成一个.class文件,为字节码文件。
- 运行阶段
- 通过写入一条命令:java 类名
- 这个.class文件被类加载器(classloader)进行加载,交给了虚拟机(JVM),是一个程序,软件,它开始读取.class文件中的类信息,把.class文件—>翻译成了0101数据(二进制)
- 二进制数据被操作系统识别(windows),交给相应的硬件进行执行,最终输出结果。
dos下
- javadoc:生成文档注释命令
例子:
把Helloworld的注释文档放进aaa文件夹下
C:> javadoc -d aaa HelloWorld.java
- jar:打压缩包(.jar)
例子:
把Helloworld.java和Helloworld2.java打包压缩民命为hello.jar
C:> jar cvf hello.jar Helloworld.java Helloworld2.java
- 导入包
HelloWorld.java导入hello文件夹下的另一个HelloWorld.java
C:> javac -d . HelloWorld.java
C:> java hello.HelloWorld
计算机编码
- ASCII码: 美国标准信息交换码:只支持英文 ,不支持中文
比如: a: 97 A: 65 - ISO-8859-1: 支持拉丁文,英文,不支持中文
- GBK : 所有中文编码统称: 支持简体(GB2312)和繁体,固定长度中文
- UTF-8:支持中文,英文 :可变长度中文
- Unicode:支持任何一种语言,包括中文,让中文在任何一台电脑上都能正确显示。
语法:\u十六进制数字
比如:\u6702;
进制表示
二进制,八进制,十六进制在计算机编程时,如何表示
二进制: 0b
八进制: 0
十六进制: 0x
例子:
二进制以零b开头如:int a = 0b0101010;
八进制以零开头如:int a = 012345;
十六进制以零x开头如:int a = 0x12345;
数据类型之间转换
- 自动类型转换(隐式类型转换):
要求:要转换的两种类型必须兼容
低优先级类型–>高优先级类型(byte–>int) - 强制类型转换:
要求:要转换的两种类型必须兼容
高优先级类型–>低优先级类型(int–>byte) - 当多种数据类型混合运算时,有限取范围大的数据类型
语法:(要强制转换的类型)数据
例子:(int)3.14
- 基本数据类型中除了
布尔
数据类型以外,剩余7种数据类型之间是可以相互转换的 - 取值范围小的数据类型是可以直接给取值范围大的数据类型赋值,构成自动类型转换,也叫做隐式类型转换
- 取值范围大的数据类型不能直接给取值范围小的类型赋值,则必须进行强制类型转换,也叫做显式类型转换,可能会存在精度有丢失
- 当对byte、short、char类型赋值时,如果没有超出当前数据类型取值范围,则是可以直接赋整数类型的值
- 当对byte、short、char类型进行混合运算时,则先转为int类型,然后再进行计算
- 当多种数据类型进行混合运算时,结果为当前运算中取值范围大的数据类型
字面量
- 概念:就是数据。
- 数据分类:
- 基本数据类型:
并且有优先级
数据类型 所占的字节大小 默认值 byte:字节 1 0 short:短整形 2 0 int:整形 4 0 long:长整形 8 0L float:单精度 4 0.0f double:双精度 8 0.0 boolean:布尔 1 false char:字符 2 \u0000 - 引用数据类型:
String:字符串
Array:数组
class:类类型
interface:接口类型
enum:枚举类型
。。。。
- 基本数据类型:
看代码
1 | int a=1; |
运算符
概念: 一些符号,这些可以参与运算,逻辑比较等操作。
算术运算符:
执行基本数学运算的符号: +,-,*,/(除法),%(取余数)逻辑运算符:
执行逻辑判断:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20&(按位与,逻辑与)
10 & 6 :按位与 (换成二进制)
条件1 & 条件2
|(按位或,逻辑或)
10 | 6 :按位或(换成二进制)
条件1|条件2: 逻辑或
!(非)
&&(短路与)
||(短路或)
^(异或)
10 ^ 6: 换成二进制,不同的为1,相同的为0。
逻辑异或^
当两边的值不同时,则结果为true,否则为false
true ^ true 结果为false
true ^ false 结果为true
false ^ false 结果为false
false ^ true 接为true比较运算符: >,>=,<,<=,!=
移位运算符 :
转换成二进制后位移- 往右移位: >>, >>>
- 往左移位: <<
1
2
3如:10>>1 //输出:5 解释:10转换成二进制后向右移动一位
//相当于除以2
10>>2 //相当于除以4
以后项目中要做除法运算,使用移位了。
三目运算符
语法格式:条件 ? 代码1 : 代码2
首先判断条件,如果条件为true,则执行代码1,否则执行代码2赋值运算符 =,+=,-=,*=,/=,%=
+=和-=都有强转的概念
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//例子1:
int a=1;
int b=2;
int c=a++; //1 a=2
int d=++b; //d=3 b=3
//最后输出2 3 1 3
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
//例子2:
int a=1;
int b=2;
a++; //2
++b; //3
//最后输出2 4
System.out.println(a++);
System.out.println(++b);
判断语句 与 循环语句
If(布尔表达式)
单if
If…else
If…else if …else(多重if选择结构)
If…if(if嵌套)
1 | // 当做等值操作时,则就可以使用switch case完成 |
循环概念:
有规律地重复做同样的事情
外层循环做一次,内层循环做一遍步骤:
书写循环变量,赋予初始值
书写循环条件
书写循环体:循环做的事情
设计循环退出的条件 ,防止循环死掉 死循环while和do…while循环区别?
while和do..while语法不同。
while先判断条件是否满足,再去执行,do…while先去执行,再判断条件是否满足。不管怎样,do…while一定会执行一次循环关键字:
break
: 退出当前循环结构continue
: 结束本轮(次)循环,进入下一轮循环循环标签(尽量不用)
使用意义:在二重循环结构中,可以直接退出最外层的循环
看代码
1 | //此处的a,b为循环标签(指针的方式),实际项目建议不要使用循环标签. |
方法(函数)
概念: 方法也叫函数,是一段用来完成特定功能的代码。
作用: 代码可以重复利用 ,提高复用性
语法: [访问修饰符] static 方法返回类型 方法名(参数列表) { }
- 方法是可以相互调用的
- 方法定义
不能
嵌套 - java中方法返回值只能返回
单个值
。 - 逻辑代码
不能
写在类体中,必须写在方法内部
当方法带有返回值类型时,如果在case或者default中编写return则就不需要再编写break,否则出现编译错误;当方法带有返回值类型必须编写返回严谨,否则就出现编译错误。
看代码
1 | public static void p(){ |
递归
方法自己调用自己
递归效率低,容易栈溢出,不建议使用
解决栈溢出的方案:
- 减少输出的次数
设置相应的条件,规定输出的次数,防止溢出 - 栈的空间大小调整
不适合于初级工程师
递归调用实际中还是有应用场合:
一些经典的数学运算
做目录的列出,可以递归,比如当前盘下的所有内容
练习:
1 | public class Test { |
关键字:return
带返回类型的方法中: return 要返回的值;
无返回类型的方法中: 直接写return即可; 代表是终止方法执行。
重载 与 重写
方法重载(overload)
- 意义:解决方法命名上的繁琐的问题,用同一个名字,就可以直接操作。
满足三条件:
同一个类中
方法名相同
参数列表不同(个数、顺序、类型)
方法重写\覆盖(override)
概念: 发生在继承结构中,把父类中继承过来的同名方法改变里面的内容。如果子类中的方法与父类中的方法签名相同,只需要改变内容即可。即外壳不变,核心重写!
签名相同:方法名相同,方法的参数列表相同,方法的返回类型相同,方法的访问修饰符相同。
如果父类中书写了静态方法,就不讨论方法重写
区别
重写是发生在继承中,重载是发生在本类中
重写必须一切都要相同,从方法名,参数类型,顺序,返回类型,访问修饰符
重载是方法名相同,方法的参数个数不同,类型不同,顺序不同。与返回类型,访问修饰符无关
看代码
1 | public class Test { |
关键字:static 与 代码块
静态代码块、属性、方法在类加载时就开始加载,所以是类级别
的
可以修饰属性、方法、代码块、内部类以及实现静态导入
- 修饰属性
public static int a=1; //静态变量,也叫静态属性
- 修饰方法
public static void show(){} ////静态方法
- 修饰代码块
static{ } //当类加载到内存时执行静态代码块,并且只执行一次
- 修饰内部类(一般不用)
1
2
3
4
5public static void show(){
Inner2 i = new Inner2();
}
static class Inner2{ //称为静态内部类,一般在外部类的静态方法中使用
} - static关键字可以实现静态导入
1
2
3
4
5
6
7
8
9
10
11
12public class MyClass {
static int y = 100;
}
import static MyClass.y;
public class Test {
public static void main(String[] args) {
//注意:当在一个类中,如果想直接使用其他类中的静态变量,则必须要实现静态导入
System.out.println(y);
}
} - 与final配合使用
1
2
3
4
5
6public class MyClass {
static final int A = 100; //表示常量,也就是值不能更改,因此建议:final关键字与static关键字配合使用,原因:当编写static关键字在内存中只存放一次,并且该类所有对象都可以共享,也就是节省空间
static final double PI = 3.14;
final static double MY_PI = 3.14159;
}
静态方法中不能访问非静态成员方法和变量
多个代码块时,自上而下,按照顺序依次加载。
静态变量初始化可以在构造方法中进行初始化,但不建议
关键字:final
概念:最终的
可以修饰三个部分:
- 属性:
属性为最终属性: 代表此属性内容不可更改
语法: public final String name=”1”; - 方法:
方法为最终方法:代表此方法不能被重写 - 类:
此类为最终类:代表此类不能被继承
加入static ,做成静态常量,常量名全大写
例子:public final static String NAME=”abc”;
看代码
1 |
|
访问权限
公共类和非公共类
当一个类前面编写public修饰符,称为公共类,则该类可以在任意包中使用
当一个类前面没有编写public修饰符,称为非公共类,则该类只能在当前包中使用
成员目前所学知识点中包含:成员变量和成员方法
访问权限 | 在本类中 | 在当前包中 | 不同包的子类 | 任意包任意类 |
---|---|---|---|---|
private私有的 | 可以使用 | 不可以使用 | 不可以使用 | 不可以使用 |
默认的 | 可以使用 | 可以使用 | 不可以使用 | 不可以使用 |
protected受保护的 | 可以使用 | 可以使用 | 可以使用 | 不可以使用 |
public公共的 | 可以使用 | 可以使用 | 可以使用 | 可以使用 |
例子:
1 | private int ages; // 私有的,如封装便需要用到 |
面向对象
- Java中有3种变量:
实例变量
:存在于堆内存中
静态变量
:存在于本地内存区域(方法区)中
局部变量
:存在与栈内存中
面向对象
对象:是实体,是生活中真实存在的事物。
类:是模板,是一个抽象的概念。把对象共同的特征和行为抽取出来。实例:对象另一种说法
实例化:类产生对象的过程 类—>对象
抽象:对象的共同特征和行为抽取出来,形成模板的过程。 对象—>类属性
概念:也叫特征;是全局变量也叫实例变量。
特点:属性以数据的方式呈现由对象所有,且每个对象都有一份实例变量有默认值,引用数据类型去做一个类的属性行为
行为也叫方法,代表此类的一个动作。
这种方法也叫实例方法。
这种方法由对象来调用,由对象所有。
方法取名一般使用动词。
构造方法
概念: 是与类同名的方法,也叫构造函数,也叫构造器
特点:方法名与类同名
比如: Student();作用:用来给属性赋值(初始化属性) 给其他对象初始化.
语法: [访问修饰符] 方法名(){ 方法体;}缺省构造器: 无参构造方法
如果类中没有声明任何构造方法,则在实例化时,会默认加载无参构造方法。
支持方法重载
在new对象时,进行调用
看代码
1 | // 题一 |
封装(private)
概念
把类的内部细节(属性,方法)进行隐藏,对外提供公开访问的方法让其他类进行访问,此种方式为封装。作用
可以防止恶意用户直接操作此属性或此方法,保证其安全性。步骤
属性私有化 private
书写读写器 getXXX() setXXX()
在写器中设置判断语句
技巧:当set返回当前对象时,在使用时就可以进行连缀操作
1 | /** |
继承\泛化(extends)
发生在至少两个类
父类(别名:超类、基类、superclass):放进其他类的共同属性和行为
子类(别名:派生类、扩展类、subclass):放入其自己独有的属性和行为
遵循 is a原则,例如:cat is an Animal
特点:减少重复代码书写、耦合度高、单一继承
继承中,父类的私有属性不能被子类。
继承中,父类的构造方法不能被子类继承。
在Java中,Object类是所有类的父类
1 | 编写父类语法格式: |
关键字:this与super
this
概念: 指代当前对象。是一个变量,是一个引用,且引用的是当前对象。
this代表的是对象本身(对象级别的);所以所以可以输出(对象地址),可以调用实例变量和方法,但存在于堆区、不能用在静态方法中
this在大部分情况下是可以省略的。
this可以在构造方法中使用,用来区分属性和局部变量(当属性和局部变量同名时)。
构造方法是可以在另外一个构造方法中调用
语法:
1 | this(); //调用无参构造方法 |
super
概念: super是关键字,所代表的是来自于父类的那一部分特征。
super的语法:
super.属性: 调用父类的属性.
super.方法: 调用父类的方法.
super不是一个变量,不能输出。
super可以认为是this的一部分,但是它没有开辟任何的内存空间只负责调用来自父类的特征。
super关键字也可以省略。
super在构造方法中的使用
子类不管发生什么情况,一定会自动去调用父类的无参构造方法
父类的无参构造方法,把它显示地声明出来。
1 | super(); 调用父类的无参构造方法 |
区别
this代表是当前对象,是一个变量,引用
super 不是一个变量,不会堆中开辟空间,super代表那个来自父类的那一部分特征
super是this的一部分
相同:
都可以调用属性,方法
都可以用在构造方法中。
都不能调用,用在静态方法。
看代码
1 | public class Test { |
多态
概念
同一种事物,由于条件不同所得到的结果也不同。(多种形态、状态)例子
水。低温: 固态;常温: 液态;高温: 气态需要满足三个步骤:
要有继承结构
要有方法重写
父类引用,子类对象
class Animal { }
class Cat extend Animal{ }
Animal animal=new Cat();
- 项目中的应用场合,一般是两种
- 作为方法的参数:父类引用做方法参数使用
service(Friend friend);
Friend friend=new ChineseFriend();
- 作为方法返回类型使用:
public Pet getPet(int typeId)
Pet pet=getPet(1);
- 作为方法的参数:父类引用做方法参数使用
(静态绑定,编译阶段)编译器会发现animal的类型是Animal类型,所以编译器会把Animal中的属性或方法给绑定上,如果属性和方法绑定上了,编译就通过;如果没有绑定上,则编译会出错;
(动态绑定,运行阶段)接下来运行的时候,发现堆内存中实际存储的对象是Cat对象, 此时,就会把Cat类中相关属性或方法绑定上。
语法
向上转型 (类似于自动类型转换): 子类—>父类(低转高)
Animal animal=new Dog();向下转型(类似于强制类型): 父类—>子类(高转低)
有概率出错
Dog dog=(Dog)animal;
看代码
1 | public class Test { |
关键字:instanceof
用来判断变量名(父类)存放的是否是类名(子类)对象,如果存放在,则对象则结果为true,否则为false。
- 语法:
if (变量名 instanceof 类名或者接口){ }
抽象(abstract)
一般在类上声明,同时可以声明抽象方法。但不能修饰属性!!!
抽象类中可以定义非抽象属性,方法,也可以定义抽象方法
抽象类不能实例化 ,实例化此抽象类下的子类
抽象父类有一个方法,子类可以选择不实现或去实现。如果子类选择了不实现,子类自己声明成abstract类即可。如果子类要实现,子类应该把此方法打开,书写方法内容。
抽象类中是否一定定义抽象方法? 不一定
抽象方法是否一定存在于抽象类中? 是的。
修饰类: 抽象类
抽象类的意义从语法角度说,里面加入一个抽象的方法语法:
1
2
3
4
5public abstract class A {
//属性
int a=1;
抽象类中可以加入实例方法,变量,静态方法,变量
}
- 修饰方法:抽象方法
语法:
public abstract void show() { }
抽象类从实际应用来说,生活中是抽象的,代码也必须设计成抽象的形状
abstract class Shape { }
接口(interface)
- 概念:
接口是一个特殊的抽象类
里面只有2个内容:静态常量,抽象方法 - 使用语法
[访问修饰符] interface 接口名 { }
接口,可以看成定义为一种能力
接口不能实例化,需要实现类来实现
接口中的静态常量一般很少写,所以认为接口中就是抽象方法
抽象类和接口都不能创建对象,也就是说:抽象类和接口都是多态的一种形式
两个多
可以多实现
可以多继承
在JDK8.0中,接口中包含:抽象方法(默认有public abstract)、公有静态常量(默认有public static final)、public static修饰的方法、public default修饰的方法
关系
在Java中,类与类之间是继承
,也就是:子类继承父类,并且是单继承
1
2
3
4class A{
}
class B extends A{
}在Java中,
接口与接口之间是继承
,也就是:子接口继承父接口,并且是多继承
1
2
3
4
5
6interface X{
}
interface Y{
}
interface Z extends X,Y{
}在JAVA中,
类与接口之间是实现
,也就是:实现类实现接口,并且是多实现
1
2
3
4
5
6interface A1{
}
interface B1{
}
class C1 implements A1,B1{
}接口不能继承类
在一个类中,
该类既有继承也有实现,则继承必须位于实现的前面
1
2public class A extends B implements X,Y {
}在Java中,接口中的抽象方法默认有
public abstract
,可以改写成如下1
2
3public interface Pet{
void eat();
}接口
不能创建对象
,也就是说:接口也是多态的一种形式
比如:Pet p = new Dog();接口也是一种数据类型,
属于引用数据类型
,在内存中存放的是地址(引用)当实现类实现接口时,必须要
重写
接口中所有的抽象方法
,否则实现类也是抽象类
大多数情况下,实现类都要重写接口中的所有抽象方法
接口和抽象类有和区别
抽象类中可以有抽象方法,还有非抽象的属性和方法;接口只有抽象方法
抽象类半抽象的;接口纯抽象。
接口和抽象类都不能实例化
抽象类只能实现单一继承,而接口多继承。面向接口编程程序设计的步骤:
定义一个接口,书写抽象方法
定义一个实现类,实现接口中的所有抽象方法
可以使用接口多态:
语法:接口类型 接口名=new 实现类类名();
看代码
1 | public interface Test01 { |
Object中有常见方法
toString()
:把一个对象变成一个字符串getClass()
:得到此对象所属类型equals()
: 判断2个对象是否相等
hashCode()
: 得到对象哈希码值finalize()
: 垃圾回收器清理不用对象
看代码
1 | // equals() : 判断2个对象是否相等,重写equals |
内部类
概念: 在一个类中再编写一个类。里面的这个类,称为内部类,也叫做内置类或者叫做嵌套类
- 成员内部类
作为外部类的一个成员 - 静态内部类
此类为静态的 - 局部内部
在方法定义中的类
1 | public class MyClass { |
- 匿名内部类(重点)
代表此类没有名字。 是一种特殊的局部内部类。
当只创建一次对象时,可以使用匿名内部类完成。
当使用匿名内部类前提,必须要继承父类或者实现接口。
1 | 语法: |
类与类之间的关系
- 继承(泛化)
子类继承父类,子接口继承父接口 - 实现
实现类实现接口 - 依赖
如果A类中方法的返回值类型或者参数列表或者局部变量使用到了B类,则称为A类依赖于B类 - 关联
如果A类中使用B类定义了成员变量,则称为A类关联B类 - 聚合
聚合也是关联的一种,如果A类中包含若干个B类,但是A类不能限定B类的生命周期,称为A类为聚合类 - 组合
组合也是关联的一种,如果A类中包含若干个B类,但是A类能限定B类的生命周期,称为A类为组合类
异常
概念:
一段程序代码在执行过程中,遇到了不正常的事件,发生了中断,这种方式为异常。异常有分类: (应用级别)
- 检查异常(checked异常)
编译时,会检查此异常,必须先去解决,否则编译不通过 - 非检查异常(unchecked异常: )
编译正确,不报错,但是运行时会引发此异常
- 检查异常(checked异常)
抛出异常遵循的是反向链的调用方式
异常结构图: api类库结构图
根类:Throwable
子类:Error(系统级)
Exception(应用级)
常见的应用级(应用级):NullPointerException ClassCastException, ArithemeticException,InputMismatchException,ArrayIndexOutOfBoundsException
…….Java异常处理机制
try: 尝试
catch: 捕获
finally: 最终,用来释放资源 ,关闭
throw: 抛: 语句级
throws:方法定义抛出异常
throw和throws
在实际工作项目中,建议throw和throws两个关键字同时书写,避免出现异常无法处理
- throw: 语句抛异常
语法:throw 要抛出的异常对象;
举例:throw new RuntimeException();
throw用在方法内部语句抛出异常
1 | public void show(){ |
- throws: 方法抛异常
语法:throws 要抛出的异常类名
举例:throws RuntimeException
throws关键字写在方法定义的小括号的后面
1 | public void show()throws 要抛出的异常类名{ |
try…catch…finally
try
必须,catch,finally
不一定具备
1 | // 捕获多个异常,多重catch选择结构。(类似if() else if()) |
自己写异常类
1 | /* |
看代码
1 | // 题目一 |
数组
概念
是引用数据类型,类似一个盒子,可以存放多个数据,并且相同的数据类型、固定的空间大小。特点
固定的空间大小,一次可以开辟一块连续的区域。
每个空间有标识符下标,作用是根据下标,找到此空间中的数据
空间大小是固定的,但可以扩充的。
数组是引用数据类型
数组中放入的数据可以是基本数据类型,也可以是引用数据类型 。数组的4大要素
数组的数据类型
数组名
数组的长度
下标优缺点
查找效率高。有连续的空间、空间大小相同、有下标、首个空间的地址就是数组地址,通过偏移量和表达式运算计算出元素所在内存地址位置
插入或删除效率比较低。会导致其他元素发生移位。
数组如果没有赋值,则会有默认值出现。
一维数组
动态声明(声明时,只开空间不赋值)
数据类型[] 数组名=new 数据类型[数组大小];
Int[] a = new int[3];
数据类型[] 数组名=new 数据类型[]{元素1,元素2,……}
int[] a = new int[] {1,2,3};
静态声明(声明时,一并赋值)
数据类型[] 数组名={元素1,元素2,……};
Int[] a = {1,2,3}
数组名[下标]=值;
扩充
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// 一对一扩充
int[] a={1,2,3};
int[] b=new int[6];
for(int i=0;i<a.length;i++) {
b[i]=a[i];
}
a=b;// b指向a,数组扩充完毕
//arraycopy方法扩充
//语法:System.arraycopy(原数组,哪里开始,新数组,新数组的开始,新数组的结束);
// 把ints列表的前三,复制到ints2空列表的前三上
public static void main(String[] args) {
int[] ints = {11, 22, 33, 44, 55, 66};
int[] ints2 = new int[10];
System.arraycopy(ints, 0, ints2, 0, 3);
// System.arraycopy(src ,srcPos ,dest ,destPos ,length); `
// 可以把src数组中从srcPos开始的length个元素复制到dest数组从destPos开始的位置上
// 把ints列表的前三,复制到ints2空列表的前三上
public static void main(String[] args) {
int[] ints = {11, 22, 33, 44, 55, 66};
int[] ints2 = new int[10];
System.arraycopy(ints, 0, ints2, 0, 3);
for (int i : ints2) {
System.out.println(i);
}
}
二维数组
概念:
二维数组是特殊的一维数组,是一维数组的嵌套。静态声明
数据类型[][] 二维数组名={二维数组中的元素1,元素2}
这里的二维数组元素是指一维数组
int[] a={};
动态声明
数据类型[][] 二维数组名=new int [长度][一堆数组长度];数据类型[二维数组的总长度][二维数组中每一个一维数组的长度]
int[] a = new int[2][3];
1 | // 二维数组遍历(类似九九乘法表) |
可变长参数
概念
可变长参数用于接收任意个数据, 即该参数接收数据的数量是可以变化的。语法
修饰符 返回值类型 方法名( 参数类型 参数名, 参数类型… 参数名){ }
说明
一个方法最多只能有一个变长参数, 并且变长参数只能放在参数列表的最后
在参数类型与参数名之间使用三个小点表示变长参数
在调用方法时,可以给变长参数传递任意个数据
在方法体中可以把变长参数当作数组使用例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public class Test06 {
public static void main(String[] args) {
//在调用方法时, 可以给变长参数传递任意个数据
sum();
sum(1);
sum(1, 2, 3, 4, 5);
sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int[] ints = {10, 20, 30};
sum(ints); //也可以给变长参数传递一个存储整数的数组
}
//定义方法,计算 若干整数 的和, 就可以使用变长参数来接收这些整数
public static void sum( int... data) {
//在方法体中可以把变长参数当作数组来用
int sum = 0;
for (int i = 0; i < data.length; i++) {
sum += data[i];
}
System.out.println("sum = " + sum);
}
}
看代码
1 | int[][] bb=new int[2][]; 对的 :二维数组长度必须先声明 |
foreach循环
快捷键:iter回车
使用foreach循环,增强的for循环遍历数组中的元素,格式为
1 | for( 数据类型 变量名 : 数组名 ){ |
说明:
数据类型就是数组中元素的类型,如果数组存储int整数则数据类型就是int, 如果数组中存储double小数则数据类型就是double, 如果数组中存储String字符串则数据类型就是String
变量名由程序员自己命名, 只在当前foreach循环中有效
执行过程, 会把数组的每个元素依次赋值给变量,再执行循环体
foreach循环与for循环都可以遍历访问数组的所有元素,但是foreach循环仅用于遍历访问
, for循环不仅可以读数组元素,还可以修改数组元素
1 | // 例子 |
十大经典排序算法
https://www.runoob.com/w3cnote/ten-sorting-algorithm.html
冒泡排序思路:从前向后两两比较, 把大的数交换到后面就实现了由小到大的排序
选择排序思路: 从当前数组元素中选择最小的交换到前面
二分查找, 前提是数组已经实现了由小到大的排序, 始终与中间的元素比较大小, 如果比中间元素小,把范围缩小到左一半, 如果比中间元素大就把范围缩小到右一半
常用方法
字符串类
- 概念
String是一个引用数据类型,代表字符串,符号是一对双引号。并且是一个常量,此常量位于常量池。
实际项目中字符串拼接不要用加号,因为会大量产生常量并且占空间。
String 字符串名=”字符串内容”;
举例: String str="hello";
String 字符串名=new String(“字符串内容”);
举例:String str=new String("world");
字节数组—>字符串
- 语法
String(字节数组byte[] ,开始,结束);
1 | byte[] bytes = {65,66,67,68,69,70,71,72,73,74}; |
字符数组—>字符串
在实际开发中,可能调用某个方法获得一个字符数组, 可以把字符数组中的字符连接为字符串
1 | char [] chars = {'A','b','6','*','汉','字',97, 98, 30686, 30988}; |
索引值 和 符串的长度
标获取此下标对应的字符String.charAt(下标)
返回字符串的长度String.length()
1 | String s = "hello进阶"; |
比较大小
比较当前字符串与参数字符串的大小(ASCII码顺序)
,如果当前字符串大返回正数, 参数字符串大返回负数,相等返回0compareTo(String anotherString)
1 | System.out.println("hello".compareTo("你好啊啊啊")); // -20216 返回负数说明 compareTo()里的数大 |
忽略大小写后再比较两个字符串的大小compareToIgnoreCase(String str)
String类定义时,实现了Comparable接口,重写它的抽象方法compareTo()方法, 逐个比较字符串的每个字符,遇到第一个不相等的字符,码值相减
1 | System.out.println("hello".compareToIgnoreCase("HELLO")); // 0 返回0,相等 |
是否包含XXX
返回的是布尔值
1 | String s = "hello进阶"; |
编码转换
返回字符串在当前默认编码下对应的字节数组byte[] getBytes()
1 | //当前编码是UTF-8 |
返回当前字符串在指定的charsetName编码下对应的字节数组byte[] getBytes(String charsetName)
1 | //当前编码是UTF-8 |
返回索引值
返回str在当前字符串中第一次出现的索引值int indexOf(String str)
返回str在当前字符串中最后一次出现的索引值int lastIndexOf(String str)
返回当前字符串中从beginIndex开始 的字符串String substring(int beginIndex)
返回当前字符串中[beginInex, endIndex)范围内的字符串String substring(int beginIndex, int endIndex)
1 | String path = "C:\\Users\\HuLu\\Desktop\\JavaProjects\\JavaSE\\src\\Test.java"; |
字符串—>符数组
1 | String s = "hello进阶"; |
大小写转换
把字符串中的大写字母转换为小写,返回新的字符串,原来的字符串不变String toLowerCase()
把字符串中的小写字母转换为大写,返回新的字符串,原来的字符串不变String toUpperCase()
1 | s1 = "Good Good Study"; |
去掉前后的空白符
去掉前后的空白符, 返回新的字符串, 原来字符串不变String trim()
1 | s1 = " Hello world "; |
类型的数据转换
把其他类型的数据转换为String字符串static String valueOf(int i)
1 | int num = 456; |
与正则匹配
String类中与正则表达式相关的几个方法
判断字符串是否匹配regex正则表达式boolean matches(String regex)
1 | Scanner sc = new Scanner(System.in); |
会把字符串中符合regex正则表达式的字符串使用replacement替换,返回替换后的字符串String replaceAll(String regex, String replacement)
1 | // 把数值变成* |
对当前字符串使用符合regex正则表达式的字符串进行拆分String[] split(String regex)
1 | String text = "A small step forward,A big step of civilization"; |
可变字符串
字符串字面量与String是不可变性
的这里不再赘述
StringBuffer
与StringBuilder
字符串缓冲区的意思。
- 工作原理
预先在内存中申请一块空间,存放字符序列。 如果空间满了,会重新改变缓冲区的大小,以容纳更多的字符序列。 - 优势
空间只开一块,容量可以扩充,大大减少内存空间的浪费。 - 使用语法:
StringBuffer 名称=new StringBuffer(); //产生一个StringBuffer对象 ,缓冲区的初始容量是16个字符
StringBuffer sb3=new StringBuffer("hello"); //产生一个StringBuffer对象,里面放入了hello的字符串,容量是字符串长度+StringBuffer的初始容量16个字符
- 不同点
StringBuffer是一个线程安全的(支持同步)
StringBuilder是线程不安全的(不支持同步)
其他用法全部一样
1 | //1) 先创建StringBuilder对象 |
常用数学类
java.lang.Math数学类
1 | //生成[0,1)范围内的随机小数 |
专门生成随机数
在java.util.Random类中
1 | //先创建Random对象 |
数字格式化
java.text.DecimalFormat类中
把数字转换 为指定格式的字符串
可以在构造方法中指定模式串, 格式符有:0
数字, 不足的位数会补0#
数字
1 | DecimalFormat df = new DecimalFormat("###,###.0000"); |
高精度(了解即可)
1 | BigInteger i1 = new BigInteger("798465884651866515461651326513265132645485616541248651486516845141651326513"); |
日期类
Date日期类
1 | //1) 创建Date对象返回当前日期 |
Time日期类
java.util.Date日期类不是线程安全的, 在JDK8中新增了一组线程安全的日期API,在java.time包中
1 | //创建LocalDateTime对象返回当前日期, LocalDateTime类的构造方法使用private修饰为私有的, 不能直接new对象了, LocalDateTime类提供一组静态方法返回该类的对象 |
Arrays类常用方法
Arrays.deepToString (二维数组)
可以把二维数组 元素连接为字符串
1 | int [][] twoInts2 = new int[6][10]; |
Arrays.toString( 数组名 )
可以把数组元素连接为字符串
1 | int[] ints = {32, 54, 76, 98, 87, 54, 12, 5 }; |
copyOf( src源数组, 新数组的长度)
可以实现数组的复制, 会根据newLength新数组长度创建一个新的数组, 把src源数组中的元素复制到新数组中, 返回新的数组
1 | //新数组长度可以比原来数组长度大,类似于数组扩容 |
Arrays.sort() 从小到大排序
调用Arrays.sort( 数组, from, to )方法可以只对象数组中[from, to)范围内的元素进行排序
1 | int[] ints = {32, 54, 76, 98, 87, 54, 12, 5 }; |
Arrays.binarySearch() 二分查找
排序后
可以二分查找。如果它包含在数组中,则返回搜索键的索引,返回负数说明不存在
1 | System.out.println( Arrays.binarySearch(ints, 5)); |
枚举(enum)
本质就是类,只不过一次可以开多个对象
使用自定义枚举类型定义变量, 赋值枚举常量值时,在常量前要加枚举类型名的前缀
当变量的值是有限的几个离散常量值时可以定义为枚举类型, 如性别, 季节等 , 通过枚举可以提高程序的可读性与健壮性
1 | public class Test { |
创建包装类对象
- 概念
每一个基本类型都有一个包装类,对基本类型的包装。 - 目的
让基本类型变成对象类型,可以使用属性和方法。
1 | 基本类型 包装类 |
包装类常用方法(以Integer为例)
1 | Integer i1 = new Integer(123); |
装箱与拆箱
1 | // jdk1.5之后:jdk新特性 |
集合
- 概念
是一个容器,大小是可以自动扩展的,里面放入的数据全是对象 - 分类
单值集合: 此集合中只放入一个值。如:list,set
键值配对集合: 也叫key-value集合,放入2个部分,一个key,一个value. 比如:map
Collection接口
常用方法
- add(Object o) : 添加元素
- remove(Object o): 删除元素
- size(): 大小
- clear(): 清空元素
- isEmpty(): 判断集合是否为空
- toArray():把集合转为数组
- contains(Object o):包含指定元素
- iterator(): 迭代方法
迭代器遍历步骤
产生集合对象,调用iterator()
方法,得到迭代器对象,返回得到是布尔值
提供迭代器对象,不断调用hasNext()
方法,看是否有下一个元素
如果有数据,则通过迭代器对象,调用next()
方法,取出下一个数据迭代器的原理:
迭代器中有一个指针,一开始指到空挡位置,并没有指向实际数据。而当调用hasNext()
方法后,指针下移,从下一个位置取出这个集合中对应的快照。(如果“快照”被破坏就报错)
List
语法
List 集合接口名=new 实现类名();特点
有序 、可重复常见独有方法
add(int index,Object o): 在指定的位置添加元素
get(int index): 根据下标获取该下标对应的元素排序
有一个对集合进行操作的工具类:Collections.sort(要排序的list集合);
LinkedList
- 概念
按照顺序从头依次接下来。 - 链表的基本单元是节点。(Node)
每一个节点中有2个部分
数据、下一个节点的地址。头,尾节点没有地址。
- 概念
比较
集合名 底层算法 查询速度 添加与删除 ArrayList 数组 快,因为有一块连续空间、下标、地址,可以计算 慢 LinkedList 链表 慢,因为分散的内存空间 快
set
特点
无序、不可重复、没下标语法
Set 集合名=new HashSet();
常见方法
大部分来自父接口CollectionHashSet
消除重复的原则
首先
先去看每个对象的Hash码,调用一个hashCode(),看每个对象的hashCode是否相等,如果不相等,则HashSet认为这是不同的元素,则把它们全部加入集合;然后
,如果Hashset值相同的,则会去调用equals()方法,看两个对象内容是否相等,如果内容不相等,则hashset认为是不同的元素,分别加入集合。最后
,如果equals()方法比较内容相同,则认为是相同元素,则会消除重复。
所以
最好把HashCode(),equals()
重写.SortedSet
(比较少用)
特点:可以排序(底层是treeMap集合)
使用: 和Set使用一样
TreeSet消除重复和排序都是重写Comparable排序接口中的comareTo()
方法做到的。和hashset中的hashCode(),equals()重写没有关系。排序
Comparable
:这种接口必须由一个类去实现,不然此接口中的方法无法实现 默认实现接口
主要重写compareTo(Object o);Comparator
: 这个接口比较灵活,分离比较规则,无需关联一个类,处理上更加人性化。
Map接口
概念
此集合中加入两个部分的值,一个是key,一个是value ,根据key找到value语法
Map map引用=new HashMap();常见独有方法:
put(Object key,Object value)
添加元素
get(Object key)
根据键,得到值
containsKey(Object key)
判断集合中是否有指定的key存在应用场景
集合在项目中一般用来临时存储数据的,首选是集合。如电商的购物车。特点
无序的,如果键重复,会覆盖相同键中原来的值HashMap算法
红黑树: 二叉树结构(平衡)
插入,删除元素都没有问题,当要插入或删除的元素比较多,出现插入或删除的效率会低下的问题。
hashmap中元素不多时,一般使用链表结构,链表搜索很复杂。遍历
1
2
3
4
5
6
7
8
9
10
11map.put("111", "aaa");
map.put("222", "bbb");
map.put("333", "ccc");
for (Object o : map.entrySet()) {
Map.Entry entry = (Map.Entry)o;
System.out.println(entry.getKey() + "\t" + entry.getValue());
}
//单独获取Key值
Set set=map.keySet();HashMap与Hashtable区别
集合名 | 特征 |
---|---|
HashMap | 键值都可以为空, ,轻量级,线程不安全(多人并发访问),访问速度快 |
Hashtable | 键值都不能为空, 重量级,线程安全,访问的速度会慢 |
如果hashmap也要线程安全就需要声明Collections.synchronizedMap(new HashMap());
排序工具类Collections.sort();
TreeMap
- 特点
可以排序,但对key排序 - 语法
Map map=new TreeMap();
使用了两种接口实现排序的
Comparator: compare(Object o1,Object o2)
Comparable: compareTo(Object o)
实际工作中常用两种集合
:ArrayList和HashMap.
- 特点
看代码
1 | /* |
泛型
概念
只能往”盒子”中放入同种数据类型。规定了集合中存放元素的具体的类型。泛型能修饰
集合,类,接口,方法。泛型的符号
<>
在集合中的语法:
单值
集合类型<集合中规定的数据类型> 集合名=new 实现类名<集合中规定的数据类型>();
key-value
集合类型<key的类型,值的类型> 集合名=new 实现类名<key的类型,值的类型>();
例子
1
2List <String> list = new ArrayList <String>();
Map <String,String> map = new HashMap <String,String> ();在类中
1
2
3
4实例化
Box<String> box = new Box<String>();
<String>后box只能输入str类型了
文件
概念
文件是把相关的一些记录放在一起,形成了数据,这些数据的集合为文件。File类常见方法
getName(): 得到文件名称
length():长度
getPath(): 获取文件绝对路径或相对路径
exists(): 判断文件是否存在
createNewFile(): 创建一个空的新文件
lastModified():最后修改时间
isDirectory()/isFile() : 是一个文件还是一个目录
getAbsolutePath(): 获取文件绝对路径
mkdir()/mkdirs(): 创建目录
delete(): 删除文件
getParent():得到文件的父目录
list(): 列出当前目录下所有的内容例子:
1
2
3File f = new File("c:/a/a.txt");
f.mkdirs();
IO流
概念
是一个通道,是用来传输数据的,一连串数据,遵循一定的方向,采用是先进先出(FIFO)方式。
归类
按照流向划分:
输入流和输出流按照数据单位划分:
字节流:能解决所有的情况。
字符流:解决字符问题。按用途划分:
打印流、对象流。。。
字节流
FileInputStream/FileOutputStream
文件输入/输出流:是一个基本的节点流
语法
FileInputStream(File file)
FileInputStream(String pathname)
方法:
read()
: 读取一个数据,数据单位就是字节
返回int类型,返回实际的数据,如果流中没有数据,则返回-1.
close()
:关闭流
flush()
刷新流
read(byte[] b)
: 把流中的数据读取到字节数组b,返回实际读取的长度文件输出流追加数据
new FileOutputStream(路径,追加(true));
String--->byte[]: 编码
byte[]--->String: 解码
缓冲输入输出流
中间有个缓冲带。所以读完与写完必须要关闭流。BufferedInputStream/BufferedOutputStream
对象流
对对象操作ObjectInputStream/ObjectOutputStream
对象输入/输出流:
特点
流的数据类型是字节,把对象转成字节流。对象写到文件中,必须先序列化操作
序列化: 把一个java对象转成字节的过程。
反序列化:把字节转成java对象的过程如果不想读取到某个属性,可以加入关键字
transient
有关对象流的面试问题
如果学生类中有一个属性它是另外一个类,请问在对象流中传输,会出问题。
没有序列化,说明了类中的属性如果是另外一个类,此类也必须序列化
在属性中使用了关键字transient
,这个会不会导致序列化终止?
序列化过程没有终止,但是属性不会通过流传递内容。
如果属性加入了static关键字,序列化是否终止?
序列化完成,但是结果也没有传递。
对于一个类,如果要实现序列化操作,建议在类中书写好此类的序列号。serialVerionUID
1 | //对象输入流读取文件中的对象 |
字符流
- 概念
流动的是单个或多个字符。此流只能处理字符串,字符。但是不能处理文本之外的数据。
基本字符流(字节到字符的桥梁)InputStreamReader/OutputStreamWriter
文件输入输出便捷类FileReader/FileWriter
构造方法中,可以加入文件路径,可以实现追加。
缺点:不能处理字符集
最常用的是BufferedReader/PrintWriter
readLine()
以行为单位读取字符串println()
通过输出流把数据写入文件
1 | //把studentDemo文本复制到student.txt下 |
其他流
PrintStream
: 字节流
过滤流—>打印字节流
手工输入流: System.in
Scanner input=new Scanner(System.in);
1 | /** |
多线程
- 概念
程序: 程序员编写的代码,那么可以运行。
进程: 正在运行中的程序。一个程序对应着一个进程。
线程: 是线程中的一个基本执行单元或执行场景,存在于进程中。一个进程包含至少一个线程,或多个线程。
线程开发方式
继承一个类: Thread类
语法: class A extends Thread {}
另外一种实现一个接口: Runnable接口
语法: class B implements Runnable {}
使用匿名类
1
2
3语法: new 要实现的接口名称/要继承父类() {
接口或父类中的方法(){}
}状态
初始状态
new对象可运行状态
调用start()运行状态
抢到cpu结束状态
运行结束阻塞状态: 当线程执行一些进入阻塞的方法后,那么会进入阻塞状态.
sleep()
: 睡眠方法(毫秒级别)
这是一个静态方法,由Thread类来调用Thread.sleep();
join()
: 加入方法
如果在一个线程中加入了另外一个线程,则会让加入的线程先执行完毕,然后当前这个线程再继续接着执行。yield()
:让出机会
如果有多个线程同时运行,可能会发生一个线程长时间占据了cpu,其他线程没有机会得到运行,为了防止此现象发生,采用了yield机制,把运行机会让出来,让其他线程能够得到运行。
使用yield,线程优先级相同时,才能给其他线程能够得到运行的机会。
设置优先级
默认5,最低1,最高10
t1.setPriority(Thread.MAX_PRIORITY); //最高优先级
t2.setPriority(Thread.MIN_PRIORITY); //最低优先级
注意
获取线程名字:Thread.currentThread().getName();
设置线程名字:Thread.currentThread().setName();
线程的打扰、暂停方法:
interrupt(),通过线程对象调用
语法:线程对象.interrupt();
线程同步
解决数据不一致问题
概念
多个线程相互争抢共同的资源(临界资源),会导致出现数据不一致,为了防止此现象,使用同步机制(锁机制)来保证数据的一致。三种锁
方法锁:在方法加入synchronized关键字
例子:1
2
3public synchronized static void show(){
}对象锁 : 先产生一个锁,锁的类型是任意类型
1
2
3
4
5
6步骤:
1.先产生一把对象锁: Object obj=new Object();
2.把对象锁放入synchronized() 中
synchronized(obj) {
要同步的代码
}this锁
1
2
3synchronized(this) {
要锁的内容放在此处
}
守护线程
概念
是一个线程,在后台默默地为其他线程提供服务。
比如:JVM中,还有一个垃圾回收线程,属于守护线程,为主线程提供服务让t1线程成为守护线程
Thread t1 = new TestThread();
t1.setDaemon(true);
典型:守护进程Timer类
线程通讯机制
- 概念
有一方处于等待状态,在等待前的,必须去唤醒另外的处于等待的消费者或生产者线程。
生产者和消费者模型, 使用的是wait()/notifyAll()
机制
- 关于
wait/notify
的注意点
此通讯机制必须使用同步
。
wait/notify
方法由对象调用,可以是任意对象,但是要确保调用这2个方法的对象,必须是同一对象
调用notify()/notifyAll()
,就会马上去唤醒那些处于等待状态的线程,让其马上工作。
有条件地调用wait()
,不然一直处于等待状态
notify()/notifyAll()
书写位置很灵活,它不限于现在前面还是后面
区别:调用wait()
后,后台会释放对象锁;调用sleep()
后,后台不会释放任何锁。
1 | /** |
新框架(了解)
jdk1.5版本后,推出了新的线程框架
线程池: ThreadPool
接口: ExecutorService
线程通讯机制更新:
wait(): await()
notify():signal()/signalAll()
synchronized(){} : lock取代
线程开发方式
实现一个接口: Callable接口
好处:能抛异常,方法有返回值。
DEMO
1 | /** |
反射(Reflection)
概念
就是操作字节码文件(class),目的是要获取字节码文件中的类的内部信息。如属性,方法,构造方法。而反射入口是Class对象。产生Class对象
代码阶段、编译阶段:Class.forName("类所在的位置");
类加载器得到:。。。。。。较少使用不写了
Class类阶段(加载阶段):类名.class
Runtime阶段:对象.getClass():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26//获取Stu类对象的三种方法,都是输出 class Stu
public class Test2 {
public static void main(String[] args) {
//你要获取哪个类的内部细节,类名就写谁
//获取学生的反射入口
//类类型 类对象
Class c = Stu.class;
System.out.println(c);
//产生对象获取Class
Stu s =new Stu();
Class c2 =s.getClass();
System.out.println(c2);
//类所在的位置获取
Class c3 = Class.forName("Stu");
System.out.println(c3);
}
}
class Stu {
public String name;
public int age;
}属性:(Field)
得到所有属性
getFields()
: 得到本类中所有的公开属性
getDeclaredFields()
:得到本类中所有的属性得到单个属性
Class.getField(String name)
: 得到公开属性
Class.getDeclaredField(String name)
: 得到所有属性
Field.set(Object obj,Object value)
: 给属性设置值 传入实际对象,实际的值
Field.get(Object obj)
: 得到属性的值注意
:对于私有属性,正常类无法直接访问,但是可以通过反射访问。如果反射要访问,必须设置属性的可访问权限。
Field.setAccessible(true);
方法: (Method):
得到所有方法:
Class.getMethods()
: 得到所有公开方法
Class.getDeclaredMethods()
: 得到所有方法得到单个方法:
Class.getMethod(String name)
: 得到单个公开方法
Class.getDeclaredMethod(String name)
:得到单个所有方法Method.invoke(Object obj, Object... args);
//解析方法(运行方法,方法有参数就带上)
构造方法: (Constructor)
- 得到所有的构造方法
Class.getConstructors()
: 得到所有公开构造方法
Class.getDeclaredConstructors()
:得到所有构造方法 - 得到单个构造方法
Class.getConstructor(Class<?>... parameterTypes)
:得到公开的单个构造方法
Class.getDeclaredConstructor(Class<?>... parameterTypes)
: 得到单个构造方法 - 如何输出构造方法中的内容:
Class.newInstance();
//调用了无参构造方法
Constructor.newInstance(Object...obj);
://调用了带参构造方法(需要clss.getConstructor(Class<?>… parameterTypes)。获取构造器后才能创建
- 得到所有的构造方法
了解其他的Class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//外部类
Class<String> clazz1 = String.class;
//接口
Class<Serializable> clazz2 = Serializable.class;
//数组
Class<Integer[]> clazz3 = Integer[].class;
//二维数组
Class<Integer[][]> clazz4 = Integer[][].class;
//注解
Class<Deprecated> clazz5 = Deprecated.class;
//枚举
Class<Thread.State> clazz6 = Thread.State.class;
//基本数据类型
Class<Long> clazz7 = long.class;
//void数据类型
Class<Void> clazz8 = void.class;
Class<Class> clazz9 = Class.class;
Field 说明
getModifiers
: 以int形式返回修饰符
说明:默认修饰符是0,public 是1,private是2,protected是4,static是8,final是16
例子:public static 修饰的返回是 9getType
:以Class形式返回类型getName
:返回属性名
Method 说明
getModifiers
: 以int形式返回修饰符
说明:默认修饰符是0,public 是1,private是2,protected是4,static是8,final是16getReturnType
:getReturnType:以Class形式获取返回类型getName
:返回方法名getParameterTypes
:以Class[]返回参数类型数组
看代码
1 | //对属性进行操作 |
学生类模板
1 | class Student { |
注解(Annotation)
概念
注解是一个引用数据类型: ,用来给属性,类,或方法等做约束使用的。jdk自带注解:
@Override
:重写
@SuppressWarnings
: 压下警告
@Deprecated
: 过时自定义注解:
步骤:先定义一个注解(像类一样
语法:1
2
3
4
5
6public 注解名称 {
注解的属性
(这里的属性按照变量的方式去写)
访问修饰符 数据类型 属性名();
}定义此注解的元注解
元注解: 注解的注解
@Target
: 目标:交代你自定义的注解,稍后打在何处(位置):可以打在类处,方法处,属性处,……
@Rentention
:保留 把注解保留在哪个地方:1)Source: 源文件中 : Test01.java
2)Class: 字节码中 : Test01.class
3)Runtime:运行时: 注解保存在字节码中,但是注解可以通过反射找到。使用你的注解
基础网络编程
Web 编程:编写程序运行在同一个网络下的两个终端上,使
得它们之间可以进行数据传输。
TCP
TCP 是面向连接的运输层协议,传输数据之前必须先建立稳
定的连接。
优点:稳定可靠,不会出现数据丢失的情况,数据是按照先
后顺序依次到达。
缺点:速度慢、效率低
服务端:ServerSocket
客户端:Socket
Server.java
1 | package TCP; |
Client.java
1 | package TCP; |
UDP
代理
静态代理和动态代理的区别
静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时动态生成,静态代理的效率相对动态代理来说相对高一些。
但是静态代理代码冗余大,一但需要修改接口,代理类和委托类都需要修改。JDK动态代理和CGLIB动态代理的区别
JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法最好不要声明成final。
例子:通过代理输出
张三使用JDK动态代理,李四使用CGLIB动态代理
Speaker接口
1 | public interface Speaker { |
张三实体实现speaker接口
1 | public class Zhangsan implements Speaker{ |
JDK代理
1 | // 需要实现 InvocationHandler |
运行
1 | import java.lang.reflect.Proxy; |
需要导包
1 | <!--代理包--> |
李四实体类
1 | public class Lisi { |
代理类
1 | import net.sf.cglib.proxy.MethodInterceptor; |
运行
1 | import net.sf.cglib.proxy.Enhancer; |
Lambda表达式
ambda表达式, 从本质来讲, 是一个匿名函数。 可以使用使用这个匿名函数, 实现接口中的方法。
对接口进行非常简洁的实现, 从而简化代码。
实际上, 我们在写lambda表达式的时候, 也不需要关心返回值类型。
我们在写lambda表达式的时候, 只需要关注两部分内容即可: 参数列表 和 方法体
基础
基础语法:
1 | (参数) -> { |
例子:
1 | // 接口 |
进阶
参数部分的精简
参数的类型
由于在接口的方法中,已经定义了每一个参数的类型是什么。 而且在使用lambda表达式实现接口的时候, 必须要保证参数的数量和类型需要和接口中的方法保持一致。 因此, 此时lambda表达式中的参数的类型可以省略不写。注意事项:
如果需要省略参数的类型, 要保证: 要省略, 每一个参数的类型都必须省略不写。 绝对不能出现, 有的参数类型省略了, 有的参数类型没有省略。参数的小括号
如果方法的参数列表中的参数数量 有且只有一个 ,此时,参数列表的小括号是可以省略不写的注意事项:
只有当参数的数量是一个的时候, 多了、少了都不能省略。
省略掉小括号的同时, 必须要省略参数的类型。
方法体部分的精简
- 方法体大括号的精简
当一个方法体中的逻辑, 有且只有一句的情况下, 大括号可以省略。 - return的精简
如果一个方法中唯一的一条语句是一个返回语句, 此时在省略掉大括号的同时, 也必须省略掉return。
- 方法体大括号的精简
函数引用
在有些情况下, 我们需要在lambda表达式中实现的逻辑, 在另外一个地方已经写好了。
此时我们就不需要再单独写一遍, 只需要直接引用这个已经存在的方法即可
注意事项:
在引用的方法后面, 不要添加小括号。
引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的一致。
- 语法:
类::静态方法
对象::非静态方法
函数引用: 引用一个已经存在的方法, 使其替代lambda表达式完成接口的实现。
1 |
|
构造方法的引用
如果某一个函数式接口中定义的方法, 仅仅是为了得到一个类的对象。 此时我们就可以使用
构造方法的引用, 简化这个方法的实现。
- 语法
类名::new
- 注意事项
可以通过接口中的方法的参数, 区分引用不同的构造方法。
1 | public class demo { |
对象方法的特殊引用
如果在使用lambda表达式,实现某些接口的时候。 lambda表达式中包含了某一个对象, 此时方法体
中, 直接使用这个对象调用它的某一个方法就可以完成整体的逻辑。 其他的参数, 可以作为调用方法
的参数。 此时, 可以对这种实现进行简化。
1 | private static class Person { |
闭包问题
如果在lambda表达式中,使用到了局部变量,那么这个局部变量会被隐式的声明为 final。 是一个常
量, 不能修改值
1 | public class Lambda4 { |
实例
- 线程的实例化
1
2
3
4
5public static void main(String[] args) {
Thread thread = new Thread(() -> {
// 线程中的处理
});
} - 集合的常见方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "我", "是", "葫芦", "呀?");
// 按照条件进行删除
list.removeIf(ele -> ele.endsWith("?"));
// 批量替换
list.replaceAll(ele -> ele.concat("!"));
// 自定义排序
list.sort((e1, e2) -> e2.compareTo(e1));
// 遍历
list.forEach(System.out::println);
}
// 最后输出:
//葫芦!
//是!
//我! - 集合的流式编程
1
2
3
4
5
6
7public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll( list, "我", "是", "葫芦", "呀?");
list.parallelStream().filter(ele -> ele.length() > 1).forEach(System.out::println);
}
// 葫芦
// 呀?