脚本宝典收集整理的这篇文章主要介绍了CGBTN2109汇总复习,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
先抓知识结构主干,再去慢慢补充细节拓展 遇到会的,快速回顾 遇到忘记或者是不会的,先记录,后面自己复习的时候着重回顾
JDK:Java开发工具包,我们使用的版本是1.8 注意:一台PC上可以安装多个JDK,具体环境变量配置哪个JDK,哪个就生效
JAVA_HOME : 配置的是JDK安装的目录 Path : 配置的是JDK的bin目录,不新建的 CLASS_PATH:配置的是JDK的lib目录 win+R键,在运行窗口输入cmd 验证命令为 : java -version 出现JDK版本号即为成功
eclipse IDEA 注意1F1a;开发工具无需纠结,重要的是编程的思路,对于工具而言,选一个自己喜欢的就好,重要的是提高这个自己常用软件的熟练度(快捷键 字体设置 配置JDK…面向百度进行开发) 注意2:大家在安装的时候,不要选择c盘系统盘,而且路径中不要出现中文或者空格等等其他特殊符号,因为会出现一些未知的问题
JDK:Java开发工具包(Java Development KIT)–开发的最小单位 JRE:Java运行时环境(Java Runtime environment)–运行的最小单位 JVM:Java虚拟机(Java Virtual Machine)–负责加载并运行.class字节码文件
50个全小写的单词,在Java有特殊的意义,还包含2个保留字const goto
字母 数字 下划线 美元符号组成,不能以数字开头,区分大小写,关键字+(true false null)也不可以用作标识符,见名知意 UpPErCamelCase大驼峰命名: 每个单词的首字母都要大写,比如类名:HelloWorld类名: Upper驼峰命名:每一个单词的首字母都要大写 LowerCamelCase小驼峰命名: 从第二个单词的首字母才开始大写,比如:方法名:nextLine() 变量名:deptName
单行注释 // 多行注释 / * * / 文档注释: /** */ 还可以添加一些额外的信息:作者 时间 版本 参数 返回值类型 注释可以注释内容,被注释的内容不执行,所以我们可以利用注释手段对进行分段代码测试
声明的时候并且赋值 Cat cat = new Cat(); 先声明再赋值 Cat cat; cat = new Cat(); 注意:基本类型保存的是值,引用类型保存的是地址值
- 如果想指定本类的成员变量,使用this.变量名来指定
- 如果想指定父类的成员变量,使用super.变量名来指定
Java的数据类型分为两类:基本类型 + 引用类型
package@H_495_126@ cn.tedu.basic;
/*本类用于测试类型的前缀*/
public class testTypePRe {
public static void main(String[] args) {
//10-2 10-1 10-0
System.out.println(100);//100-10的平方
//操作二进制-0b
//2-2 2-1 2-0
System.out.println(0b100);//4-2的平方
//操作八进制
//8-2 8-1 8-0
System.out.println(0100);//64-8的平方
//操作十六进制
//16-2 16-1 16-0
System.out.println(0X100);//256-16的平方
}
}
口诀:小转大,直接转 大转小,强制转 浮变整,小数没
package cn.tedu.basic;
/*本类用来测试类型转换
* 1.byte1--short char2--int4--long8--float4--double8
* 2.小到大 直接转:隐式转换,可以直接转换
* 大到小 强制转:显式转换,需要强转,注意发生数据溢出的问题
* 浮变整 小数没:小数部分直接被舍弃
* 3.强制类型转换的格式:目标类型 变量名 = (目标类型)要转换类型的数据;
* */
public class TestTypeChage {
public static void main(String[] args) {
byte a = 10;
short b = a;//不会报错,小转大
int c = 1;
long d = c;//不会报错,小转大
float f = 3.1415f;
double e = f;//不会报错,小转大
long g = 8274624867L;
float h = g;//不会报错,小转大
System.out.println(h);
char i = 'a';
int j = i;//不会报错,小转大
System.out.println(j);//97
int a1 = 1;
byte b1 = 2;
//byte c1 = a1+b1;会报错,大转小
byte c1 = (byte) (a1+b1);//需要强制类型转换
byte d1 = (byte) 128;
System.out.println(d1);//-128,需要强转,注意发生数据溢出的问题
short e1 = 'a';
char f1 = 120;
System.out.println(e1);//97,打印的是编码值
System.out.println(f1);//'x',打印是根据编码找到的字符
float h1 = 32874.435F;
int i1 = (int) h1;//大转小 强制转
System.out.println(i1);//32874 浮变整 小数没
}
}
package cn.tedu.basic;
public class Testoperator {
public static void main(String[] args) {
//引用类型Cat类型的变量c1 c2 保存的是对象的地址值
Cat c1 = new Cat();
Cat c2 = new Cat();
int[] a1 = {1, 2, 3};
int[] a2 = {1, 2, 3};
int b1 = 4;
int b2 = 4;
boolean f1 = true;
boolean f2 = true;
/* == 如果比较的是基本类型,比较的是值,字面值,具体存的那个数*/
System.out.println(b1 == b2);//true
System.out.println(f1 == f2);//true
/* == 如果比较的是引用类型,比较的也是值,地址值*/
System.out.println(c1 == c2);//false
System.out.println(a1 == a2);//false
}
}
class Cat {
String name;
int age;
public void bark() {
System.out.println("小猫喜欢喵喵叫");
}
}
顺序结构中的代码会按照顺序一行一行向下执行所有的代码语句,可以用来进行输入 输出 计算等的操作 但顺序结构不可以完成先做判断,再做选择的流程
if(判断条件){
如果判断条件的结果为true,就执行此处代码,不符合条件,此处跳过
}
if(判断条件){
如果判断条件的结果为true,就执行此处的代码
}else{
如果不符合条件,执行else处的代码
}
if(判断条件1){
符合判断条件1,执行此处代码,不符合,继续向下判断
}else if(判断条件2){
符合判断条件2,执行此处代码,不符合,继续向下判断
}else if(判断条件3){
符合判断条件3,执行此处代码,不符合,继续向下判断
}else{
保底选项,以上条件均不符合的情况下,执行此处代码
}
package cn.tedu.basic;
import java.util.Scanner;
/*本类用于复习分支结构*/
public class TestIf {
public static void main(String[] args) {
//1.提示并接收用户输入的月份
System.out.println("请输入您要查看的月份:");
int month = new Scanner(System.in).nextInt();
//2.对用户输入的数据进行合法性检测
/*如果if后的语句只有一句,大括号可以省略不写*/
/*return关键字除了可以帮我们返回方法的返回值以外
* 还可以直接结束当前的方法,如果遇到了return,本方法会直接结束*/
// if(month <1 || month >12) return;
// System.out.println("今天天气真不错");//这句话是用来检测main()有没有结束
if(month <= 0 || month >12){
System.out.println("您输入的月份不正确!应该是1-12月以内");
}else{
//3.判断接收到的合法数据属于哪个季节,并将结果输出
if(month >=3 && month <=5){
System.out.println(month+"月是春天");
}else if(month >=6 && month <=8){
System.out.println(month+"月是夏天~");
}else if(month >=9 && month <=11){
System.out.println(month+"月是秋天");
}else{
System.out.println("冬天就要来啦,春天还会远吗?");
}
}
}
}
switch (变量名){
case value1 : 操作1;break;//可选
case value2 : 操作2;break;//可选
case value3 : 操作3;break;//可选
case value4 : 操作4;break;//可选
default:保底选项;//可选
}
package cn.tedu.basic;
import java.util.Scanner;
/*本类用于复习switch*/
public class TestSwitch {
public static void main(String[] args) {
//需求:接收用户今天输入的颜色,推荐菜品
//1.提示并接收用户输入的颜色
System.out.println("请输入您今天心情的颜色:");
String color = new Scanner(System.in).nextLine();
//2.完成选择结构
switch (color) {
case "红":
System.out.println("红烧鱼");
break;//为了避免穿透
case "黄":
System.out.println("菠萝炒饭");
break;
case "橙":
System.out.println("水煮肉片");
break;
default:
System.out.println("哎呀,没有识别到这个功能呢,正在开发中...");
}
}
}
可以帮我们多次重复的做某一件事 1.for循环
for(开始条件;循环条件;更改条件){
如果符合循环条件,就会执行循环体里的内容
}
注意1:写法小窍门:从哪开始 到哪结束 循环变量如何变化 注意2:for循环能够执行多少次,取决于循环变量可以取到几个值 2.嵌套for循环 外层循环控制的是轮数,内层循环控制的是每一轮中执行的次数 对于图形而言,外层循环控制的是行数,内层循环控制的是列数
for(开始条件;循环条件;更改条件){//外层循环
for(开始条件;循环条件;更改条件){//内层循环
循环体
}
}
注意:外层循环控制的是行数,内层循环控制的是列数 注意:外层循环控制的是轮数,内层循环控制的是在这一轮中执行的次数 3.高效for循环
for(遍历到的元素的类型 遍历到的元素的名字 :要遍历的数组/集合名){
循环体
}
优点:写法简单,效率高 缺点:只能从头到尾的遍历数据,不能进行步长的选择 4.while循环
while(判断条件){
如果符合判断条件,继续循环
}
注意:常用来完成死循环,但死循环必须设置出口! 练习题:while循环练习
package cn.tedu.basic;
/*本类用作while复习*/
public class TestWhile {
public static void main(String[] args) {
//需求1:通过while循环打印10次"小可爱们中午好~"
f1();
//需求2:通过while循环打印1 2 3 ... 10
f2();
//需求3:通过while循环打印1 3 5 7 9 ... 99
f3();
//需求4:通过while计算:1+2+3+4+...+10
f4();
//需求5:通过while计算:2+4+6+8...+100
f5();
}
private static void f1() {
//需求1:通过while循环打印10次"小可爱们中午好~"
//1.定义一个变量用来控制次数
int count = 1;
//2.定义一个循环,结束条件是count>10
while (count <= 10) {
System.out.println("小可爱们中午好~");
count++;//注意count的值需要自增,不然就是一个死循环
}
System.out.println(count);//11
}
private static void f2() {
//需求2:通过while循环打印1 2 3 ... 10
int i = 1;
while (i <= 10) {
System.out.println(i);
i++;
}
}
private static void f3() {
//需求3:通过while循环打印1 3 5 7 9 ... 99
int num = 1;
while (num < 100) {
System.out.println(num);
//num++;//12345...
//num += 2;
num = num + 2;//13579...
}
}
private static void f4() {
//需求4:通过while计算:1+2+3+4+...+10
int i = 1;//用于控制循环,相当于循环变量
int sum = 0;//用来保存求和的结果
while(i <= 10){
sum += i;
//sum = sum + i;
i++;
}
System.out.println("1到10累加的结果为:"+sum);
}
private static void f5() {
//需求5:通过while计算:2+4+6+8...+100
int i = 2;//循环变量控制循环
int sum = 0;//用来保存求和的结果
while(i <= 100){
sum += i;//累加
i += 2;//循环变量递增
}
System.out.println("2到100累加的结果为:"+sum);
}
}
5.do-while循环 do-while循环一定会执行一次,然后再判断,如果符合条件,再执行后面的循环
do{
循环体
}while(判断条件);
循环之间的比较
package cn.tedu.basic;
/*本类用于练习方法的调用*/
public class MethodDemo {
//1.创建入口函数
public static void main(String[] args) {
System.out.println(";main() is start...");
m1();
System.out.println("main() is stop...");
}
//2.创建m1()
private static void m1() {
System.out.println("m1() is start...");
m2();
System.out.println("m1() is stop...");
}
//3.创建m2()
private static void m2() {
System.out.println("m2() is start...");
}
}
9.拓展 : 方法的递归
package cn.tedu.basic;
/*本类用于复习数组的操作*/
public class TestArray1 {
public static void main(String[] args) {
//需求1:求出数组中所有的元素之和
f1();
//需求2:求出数组中所有元素的最大值
f2();
//思考题:将数组中所有元素逆序输出
//举例:原数组:1 2 3 4 5
//输出效果:5 4 3 2 1
}
private static void f2() {
//需求2:求出数组中所有元素的最大值
//1.定义一个数组
int[] a = {45, 8, 90, 34, 65, -9};
//2.定义一个变量,用来存储结果,也就是数组中所有元素的最大值
int max = a[0];//这个位置不能写0,应该是数组中第一个元素的值
//3.遍历数组,比较出最大值
for (int i = 0; i <= a.length - 1; i++) {
//4.判断max与a[i]的大小
if(a[i] > max){
max = a[i];//让max保存的一直都是目前遍历到的最大值
}
}
//4.循环结束,输出数组中的最大值
System.out.println("最大值为:"+max);
}
private static void f1() {
//需求1:求出数组中所有的元素之和
//1.定义一个数组
int[] a = {1, 2, 3, 4, 5};
//2.定义一个变量用来保存最终的结果
int sum = 0;
//3.用数组的遍历来进行数据的累加
//i:下标 0 a.length-1 ++
for (int i = 0; i <= a.length - 1; i++) {
sum += a[i];
}
System.out.println("数组元素累计的和为:" + sum);
}
}
位置:类里方法外 执行时机:随着类的加载而加载,最先加载到内存,优先于对象进行加载,直到类小消失,它才会消失 作用:一般用来加载那些只需要加载一次并且第一时间就需要加载资源,称作:初始化
位置:类里方法外 执行时机:创建对象时执行,创建几次,执行几次,并且优先于构造方法执行 作用:用于提取所有构造方法的共性功能
位置:方法里 执行时机:当其所处的方法被调用时才会执行 作用:用于限制变量的作用范围,出了局部代码块就失效
静态代码块 -> 构造代码块 -> 构造方法 -> 普通方法【如果普通方法里有局部代码块,局部代码块才会执行】
Java的类只支持单继承,类与类就是继承关系,并且一个子类只能有一个父类 class Son extends Father{ }
Java的接口是不做限制的,可以多继承 interface Inter1 extends Inter2{ } – Inter1是子接口 Inter2 是父接口 interface Inter1 extends Inter2,Inter3{ } – Inter1 是子接口 Inter2 和 Inter3 都是父接口 注意:如果是情况2的话,接口1的实现类需要实现这三个接口(Inter1,2,3)的所有抽象方法
Java中的类对于接口而言是多实现的,所以一个类可以实现多个接口 class InterImpl implements Inter1{} class InterImpl implements Inter1,Inter2{}
try{
可能会出现异常的代码
}catch(预测的异常类型 异常的名字){
预先设计的,捕获到异常的处理方案
}finally{
异常处理结构中一定会被执行到的代码块,常用来关流
}
/*外部类名.内部类名 对象名 = 外部类对象.内部类对象*/
Outer.Inner oi = new Outer().new Inner();
成员内部类 位置:类里方法外 1)被private修饰 被私有化的内部类在main()中是没有办法直接创建其对象的 可以在私有内部类所处的外部类中,创建一个公共的方法供外界调用,这个方法用来返回创建好的私有内部类对象 2) 被static修饰 静态内部类可以不创建外部类对象,直接创建静态内部类对象,格式:Outer3.Inner3 oi = new Outer3.Inner3(); 如果静态内部类中还有静态方法,那么我们可以不创建对象 直接通过链式加载的方式调用:Outer3.Inner3.show2();//表示通过外部类名直接找到静态内部类,再找到静态方法 局部内部类 位置:方法里 直接创建外部类对象,调用局部内部类所处的方法,并不会触发局部内部类的功能 需要在外部类中创建局部内部类的对象并且进行调用局部内部类的功能,才能触发内部类的功能 匿名内部类 位置:可运行代码中,比如 main()中 匿名内部类通常与匿名对象【没有名字的对象】一起使用 格式:new Inter1(){ 我这个大括号其实是一个匿名内部类,我来实现方法 }.eat(); 如果只是想使用一次接口/抽象类的某个功能,可以使用匿名内部类 匿名内部类+匿名对象的功能:创建实现类+实现方法+方法功能的一次调用【功能三合一】
int hashCode() 返回此字符串的哈希码。 boolean equals(Object anObject) 将此字符串与指定的对象比较,比较的是重写后的串的具体内容 String toString() 返回此对象本身(它已经是一个字符串!)。
int length() 返回此字符串的长度。 String toUpperCase() 所有字符都转换为大写。 String toLowerCase() 所有字符都转换为小写 boolean startsWith(String prefix) 测试此字符串是否以指定的元素开头。 boolean endsWith(String suffix) 测试此字符串是否以指定的字符串结束。
char charAt(int index) 返回指定索引/下标处的 char 值/字符 int indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引。 int lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引。 String concat(String str) 将指定字符串连接/拼接到此字符串的结尾,注意:不会改变原串 String[] split(String regex) 根据给定元素来分隔此字符串。
String trim() 返回去除首尾空格的字符串 byte[] getBytes() 把字符串存储到一个新的 byte 数组中 String substring(int beginIndex) 返回一个新子串,从指定下标处开始,包含指定下标 String substring(int beginIndex, int endIndex) 返回一个新子串,从执定下标开始,到结束下标为止,但不包含结束下标 static String valueOf(int i) 把int转成String
String的:
StringBuilder的:
package cn.tedu.api;
/*本类用于测试自动装箱与自动拆箱*/
public class TestNumber2 {
public static void main(String[] args) {
//1.定义包装类型的数据
Integer i1 = new Integer(127);
Integer i2 = Integer.valueOf(127);
//2.现在的方式:
Integer i3 = 5;//不会报错,这个现象就是自动装箱
int i4 = i1;//不会报错,这个现象就是自动拆箱
}
}
InputStream--抽象父类--不能实例化
FileinputStream--文件字节输入流-FIS
BufferedInputStream--高效字节输入流-BIS
FIS in = new FIS(new File(路径));
FIS in = new FIS(路径);
BIS in = new BIS( new FIS(new File(路径)));
BIS in = new BIS(new FIS(路径));
OutputStream--抽象父类,不能实例化
FileOutputStream--文件字节输出流--fos
BufferedOutputStream--高效字节输出流-BOS
FOS out = new FOS(new File(路径));
FOS out = new FOS(路径);
BOS out = new BOS(new FOS(new File(路径)));
BOS out = new BOS(new FOS(路径));
Reader--抽象父类--不能实例化
FileReader--文件字符输入流-FR
BufferedReader--高效字符输入流-BR
FR in = new FR(new File(路径));
FR in = new FR(路径);
BR in = new BR(new FR(new File(路径)))
BR in = new BR(new FR(路径));
Writer--抽象父类,不能实例化
FileWriter--文件字符输出流--FW
BufferedWriter--高效字符输出流--BW
FW out = new FW(File/File,append/String pathname/String pathname,append);
BW out = new BW(Writer--所以传的是子类FW(上面那4种));
注意:这里的append参数表示流向文件输出数据的时候是追加还是覆盖,如果不写,默认false是覆盖,如果改为true,表示追加
单个集合的操作:
boolean add(E e) 将指定元素添加到集合中 void clear() 清空集合 boolean contains(Object o) 判断本集合是否包含指定的元素 boolean equals(Object o) 比较集合对象与参数对象o是否相等 int hashCode() 返回本集合的哈希码值 boolean iSEMpty() 判断本集合是否为空 boolean remove(Object o) 从本集合中移除指定元素o int size() 返回本集合中元素的个数 Object[] toArray() 将本集合转为数组
集合间的操作:
boolean addAll(Collection<> c) 将c集合中的所有元素添加到本集合中 boolean containsAll(Collection<> c) 判断本集合是否包含c集合的所有元素 boolean removeAll(Collection<> c) 移除本集合中属于参数集合c的所有元素 boolean retainAll(Collection<> c) 保留本集合与参数集合c的公共元素
集合的迭代:
Iterator iterator() 返回本集合的迭代器
单个集合的操作:
void add(int index, E element) 在集合的指定下标index处插入指定元素element E get(int index) 返回本集合中指定下标index处的元素 E remove(int index) 移除本集合中指定下标index处的元素 E set(int index, E element) 用参数元素element替换集合中指定下标index处的元素 int indexOf(Object o) 判断指定元素o在本集合中第一次出现的下标,如果不存在,返回-1 int lastIndexOf(Object o) 判断指定元素o在本集合中最后一次出现的下标,如果不存在,返回-1 List subList(int FromIndex, int toIndex) 截取子集合,包含formidex处的元素,不包含toIndex处的元素
集合间的操作与集合的迭代
boolean addAll(int index, Collection<> c) 将参数集合c中的所有元素,插入到本集合中指定的下标index处 ListIterator listIterator() 返回此列表元素的迭代器,这个是List自己的,不太常用,可以逆序迭代
简单方法:
void addFirst(E e) 添加首元素 void addLast(E e) 添加尾元素 E removeFirst() 删除首元素 E removeLast() 删除尾元素 E getFirst() 获取首元素 E getLast() 获取尾元素 E element() 获取首元素
功能一致但是名字不太好记的方法:
boolean offer(E e) 添加尾元素 boolean offerFirst(E e) 添加首元素 boolean offerLast(E e) 添加尾元素 E peek() 获取首元素 E peekFirst() 获取首元素 E peekLast() 获取尾元素 E poll() 返回并移除头元素 E pollFirst() 返回并移除头元素 E pollLast() 返回并移除尾元素
简单方法:
void clear() 清空集合 boolean equals(Object o) 判断集合对象与参数o是否相等 int hashCode() 返回本集合的哈希码值 boolean isEmpty() 判断集合是否为空 int size() 返回本集合中键值对的个数
map单个集合间的操作
boolean containsKey(Object key) 判断map中是否包含指定的key boolean containsValue(Object value) 判断map中是否包含指定的value V get(Object key) 根据指定的key返回对应的value,如果不存在,返回null V remove(Object key) 删除本集合中参数key对应的键值对 V put(K key, V value) 向集合中添加映射关系(键值对) void putAll(Map<> m) 向本集合中添加m集合的所有映射关系(键值对)
map的迭代
Collection values() 把本map中的Value值取出放入一个Collection中并返回这个Collection Set keySet() 把本map中的Key值取出放入一个Set集合中并返回这个Set集合 Set<Map.Entry<K,V>> entrySet() 把本map中的每一对KV都看成是一个Entry,把所有的Entry取出放入一个Set集合中并返回这个Set集合
程序:数据与指令的集合,而且程序是静态的
进程:运行中的程序,给程序加入了时间的概念,不同时间进程有不同的状态,进程是动态的,代表OS中正在运行的程序 进程有独立性,动态性,并发性
并行:相对来说资源比较充足,多个CPU同时并发处理多个不同的进程
串行:相对来说资源不太充足,多个资源同时抢占公共资源,比如CPU
线程:线程是OS能够进行运算调度的最小单位 一个进程可以拥有多个线程,当然,也可以只拥有一个线程,只有一个线程的进程称作单线程程序 注意:每个线程也有自己独立的内存空间,当然也有一部分共享区域用来保存共享的数据
线程的几种状态以及线程状态之间的切换 1)新建状态:创建线程对象,申请PCB,对应的是new线程对象 2)就绪状态/可运行状态:万事俱备,只欠CPU,刚刚创建好的线程对象所有资源已经准备好,并且加入到了就绪队列之中 唯有等待操作系统的调度,只要分配了CPU,也就是时间片,当前线程可立即执行,对应的是start() 注意:调用start()并不会立即执行线程对象,这个是由OS的调度规则决定的。我们控制不了 3)执行/运行状态:就绪队列中的线程对象被OS选中,分配了时间片,正在执行 注意:只有就绪状态才能变成运行状态 4)阻塞状态:线程在执行过程中遇到了问题,比如锁阻塞、休眠阻塞、等待阻塞… 注意:我们的阻塞状态,等问题解决了以后/获取了临界资源【要抢占的公共资源】后 是加入到就绪队列中的,转为就绪状态,而不是转为运行状态直接执行 5)终止状态:线程成功执行完毕,释放资源,归还PCB 6)线程的挂起:正在运行中的线程,由于CPU分配的时间片已经用完,所以需要冻结当前线程运行的状态与各项信息 把它插入到就绪队列中,直到下次这个线程被调度执行时,重新恢复现场,继续执行
多线程编程实现方案一:extends Thread继承方式 1)自定义一个多线程类用来继承Thread类 2)重写run()里的业务【这个业务是自定义的】 3)创建线程对象【子类对象】 4)通过刚刚创建好的自定义线程类对象调用start() 注意1:不能调用run(),因为这样调用只会把run()看作一个普通的方法,并不会以多线程的方式启动程序 而且调用start()时,底层JVM会自动调用run(),执行我们自定义的业务 注意2:我们除了可以调用默认的父类无参构造以外,还可以调用Thread(String name),给自定义的线程对象起名字,相当于super(name);
多线程编程实现方案二:implements Runnable 实现方式 1)自定义一个类实现接口Runnable 2) 实现接口中唯一一个抽象方法run() 3) 创建接口实现类的对象,这个对象是作为我们的目标业务对象【因为这个自定义类中包含了我们的业务】 4)创建Thread类线程对象,调用的构造函数是Thread(Runnable target) 5)通过创建好的Thread类线程对象调用start(),以多线程的方式启动同一个业务target 注意1:由于Runnable是一个接口,无法创建对象,所以我们传入的目标业务类,也就是接口实现类的对象 注意2:只有调用start()才能把线程对象加入到就绪队列中,以多线程的方式启动,但是: 接口实现类与接口都没有这个start(),所以我们需要创建Thread类的对象来调用start(),并把接口实现类对象交给Thread(target); 大家可以理解成“抱大腿”,创建的是Thread类的线程对象,我们只需要把业务告诉Thread类的对象就好啦 使用方式二的优势: 1)耦合性不强,没有继承,后续仍然可以继承别的类 2)采用的是实现接口的方式,后续仍然可以实现别的接口 3)可以给所有的线程对象统一业务,业务是保持一致的 4)面向接口编程,有利于我们写出更加优雅的代码
多线程编程实现方案三:Executors 创建线程池的方式 1)创建线程池的工具类:Executors.newFixedThreadPool(int n);可以创建包含最多n个线程的线程池对象 2)创建好的线程池对象:ExecutorService 使用pool.excute()来讲线程池中的线程以多线程的方式启动,每次调用都会将一个线程对象加入到就绪队列之中 这个线程池对象负责: 新建/启动/关闭线程,而我们主要负责的是自定义的业务 注意:线程池是不关闭的,实现的效果就是线程池中线程对象的随取随用,这样就避免了频繁的创建与销毁线程,不会造成资源浪费 3)合理利用线程池可以拥有的优势: 1. 降低系统的资源消耗:减少系统创建与销毁线程对象的次数,每个线程都可以重复利用,执行多次任务 2. 提高响应速度:当任务到达时,任务可以不用等待线程创建就能立即执行 3. 提高线程的可管理性:可以根据系统的承受能力,调整线程池中线程的数目 防止因为创建多个线程消耗过多的内存导致服务器的崩溃 【每个线程大约需要1MB的内存,线程开的越多,消耗的内存也就越大,最后死机】
多线程数据安全隐患的解决方案 1)出现数据安全问题的原因:多线程程序 + 多个线程拥有共享数据 + 多条语句操作共享数据 2)解决:加锁synchronized同步关键字 1. 同步方法【不太常用】,格式:在方法的定义上加synchronized 2. 同步代码块,格式:
synchronized(唯一的锁对象){
可能会出现数据不安全问题的所有代码
}
注意1:锁对象必须唯一!!!
比如:如果是实现接口的方式,只需要创建一个接口实现类对象。而这个对象当做的是目标业务对象,类中定义的锁对象自然也只有一个
比如:如果是继承Thread类的方式,我们需要创建多个子类对象作为线程对象
那这个时候不同的线程对象间应该共享同一把锁,所以需要把锁设置为静态,被全局所有对象共享
而且建议,此种方式使用的锁对象是本类的字节码对象:类名.class
注意2:加锁时,同步代码块的范围需要考虑, 不能太大,太大效率太低;也不能太小,太小锁不住
注意3:加锁时,锁对象的类型不做限制,只要保证锁对象唯一即可
同步与异步 异步:是多个线程抢占资源的效果,不排队,所以效率高,但是数据不安全 同步:每次只有一个线程独占资源,排队,所以效率低,但是安全,为了安全必要应该牺牲一部分资源
静态
–需要跳过对象,通过类名直接调用这个返回本类对象的公共方法 对象也需要设置成静态
的–这个对象需要在静态方法中被返回,而静态只能调用静态 方案二:懒汉式 ==延迟加载的思想:==我们有的时候有些资源并不是需要第一时间就创建出来,所以需要延迟到需要时再创建,这样既可以提升性能,又可以节省资源 1)把本类的构造方法私有化–为了不让外界调用构造函数来创建对象 2)创建了一个本类类型的引用类型变量
【这个变量后续用来保存创建出来的对象的地址值】 3)提供公共的全局访问点向外界返回本类的唯一的一个对象 注意:这个公共的方法里,需要做判断 如果引用类型的变量值为null,说明:之前没有创建过本类对象–创建后再赋值给引用类型变量,并把它返回 如果引用类型的变量值不为null,说明: 之前有创建过本类对象,这个引用类型变量保存就是地址值,本次不再新建对象,直接返回获取字节码对象 Class.forName(“类的全路径”); 注意:传入的是类的全路径名,包含包名.类名,而且会抛出异常 类名.class 注意:这个写法需要自己手动接一下获取到的字节码对象,不能用快捷方式的 对象.getClass(); 注意:经常与匿名对象一起使用 获取包名 类名 clazz.getPackage().getName()//包名 clazz.getSimpleName()//类名 clazz.getName()//完整类名 获取成员变量定义信息 getFields()//获取所有公开的成员变量,包括继承变量 getDeclareDFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量 getField(变量名) getDeclaredField(变量名) 获取构造方法定义信息 getConstructor(参数类型列表)//获取公开的构造方法 getConstructors()//获取所有的公开的构造方法 getDeclaredConstructors()//获取所有的构造方法,包括私有 getDeclaredConstructor(int.class,String.class) 获取方法定义信息 getMethods()//获取所有可见的方法,包括继承的方法 getMethod(方法名,参数类型列表) getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法 getDeclaredMethod(方法名,int.class,String.class) 反射新建实例 clazz.newInstance();//执行无参构造创建对象 clazz.getConstructor(int.class,String.class)//要先获取构造方法 c.newInstance(666,”海绵宝宝”);//通过获取到的构造函数对象,创建目标类对象 反射调用成员变量 clazz.getDeclaredField(变量名);//获取变量 field.setAccessible(true);//使私有成员允许访问 field.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null field.get(实例);//访问指定实例变量的值,静态变量,第一参数给null 反射调用成员方法 Method m = Clazz.getDeclaredMethod(方法名,参数类型列表); m.setAccessible(true);//使私有方法允许被调用 m.invoke(实例,参数数据);//让指定实例来执行该方法
以上是脚本宝典为你收集整理的CGBTN2109汇总复习全部内容,希望文章能够帮你解决CGBTN2109汇总复习所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。