华清远见-重庆中心-JAVA高级阶段技术总结/个人总结
目录
String字符串
定义
字符串对象的含义
关于字符串对象内存地址的问题
1.使用“”赋值创建字符串对象
2.通过构造方法创建字符串对象
3.使用+拼接""和new出来的字符串对象创建字符串对象 (难点)
比较字符串时为什么要使用equals方法而不用“==”
两者比较的本质
使用equals方法的注意点
可变字符串
使用StringBuilder和StringBuffer时的注意点
可变字符串相关面试题
方法调用的传值问题
与八大原始类型对应的包装类
异常
方法
try-catch-finally
try-catch-finally相关面试题
throws关键字
数组和集合的区别
泛型
集合家族
Collection接口
常用方法
lterable接口为什么不是集合的根接口
Collections集合工具类
list接口
实现类接口-ArrayList接口
实现类接口-LinkedList接口
set接口
实现接口-HashSet接口
实现接口-TreeSet接口
特点
集合和数组之间的转换
Map接口
实现类-HashMap接口
遍历集合中元素的方式
数组管理系统 VS 集合管理系统
使用数组完成-ATM系统
使用(List集合)ArrayList实现-新闻管理系统
使用(Map集合)HashMap实现-用户管理系统
文件类File
IO
流Stream
使用FileInputStream和FileOutputStream实现-单文件的复制
文件夹的复制
序列化和反序列化应用
网络编程
InetAddress类
Socket类和ServerSocket类
使用套接字对象实现两个端点(Socket和ServerSocket)之间发送文件
进程和线程
并行和并发
同步和异步
Java中的线程Thread类
实现多线程
生命周期
守护线程
多线程访问同一个资源
死锁
多线程相关面试题
个人总结
String字符串
定义
是一个属于数据类型的引用类型,本质是一个类。
字符串对象的含义
使用“”引起来的内容
关于字符串对象内存地址的问题
1.使用“”赋值创建字符串对象
String name = "Java";name = "OK";Strng major = "Java";String thing = "Ja" + "va"
说明:
- 第一段语句:创建了“Java”的字符串对象,确实将这个字符串对象的地址保存在了变量name中。(在字符串常量池中没找到“Java”字符,就会创建一个新的“Java”字符串对象)
- 第二段语句:表面上是重新给name变量赋了值,实质是创建了新的“OK”字符串对象。(过程就是执行时,发现“OK”在字符串常量池中不存在,就会创建一个新的“OK”字符串对象)
- 第三段语句:表面上创建了新的字符串对象,其实是直接引用“Java”字符对象(这时它已经在常量池中找到了,所以就直接使用。)
- 第四段语句:+两端都是""定义的字符串,值得注意的是:它是在拼接后再判断字符串常量池(缓冲区)中是否存在(拼接后的"Java"依然存在,将其地址保存到thing变量中)
以上变量(除却name创建新的“OK”字符串对象)的内存地址都是相等的(用的“==”做判断)
2.通过构造方法创建字符串对象
String str1 = "Java";String str2 = "Java";
说明:
- 第一段语句:在字符串常量池中寻找"Java",不存在,所以在堆中newString(),将字符串常量池中的"Java"保存到new出来的区域,最后将堆中new出来的地址保存到栈中变量str1中。
- 第二段语句:在字符串常量池中寻找"Java",存在,直接引用,同样的在堆中newString(),将字符串常量池中的"ab"保存到new出来的区域,最后将堆中new出来的地址保存到栈中变量str2中。
以上str1和str2的内存地址不同(用的“==”做判断)(原因:str1和str2是堆中的两个区域)
3.使用+拼接""和new出来的字符串对象创建字符串对象 (难点)
String str2 = "Ja" + new String("va")
说明:
- 创建StringBuilder对象
- 在字符串常量池中创建"Ja"
- 在字符串常量池中创建"va"
- 创建String对象,保存"va"的地址
- 调用StringBuilder的append方法,将"Ja"和newString("va")拼接
以上过程一共创建4个对象("Ja","va",String, StringBuilder对象)
比较字符串时为什么要使用equals方法而不用“==”
两者比较的本质
- “==”判断的是内存地址
- 使用String类重写的equals方法进行判断,就是将两个字符串用字符数组保存,逐个判断字符数组中的每个字符,全部一致时返回true,所以比较的是字面值。
使用equals方法的注意点
要将已知不为空的字符串作为调用者,才不会出现空指针异常的问题。
可变字符串
使用StringBuilder和StringBuffer时的注意点
- 普通方法都是在直接操作同一个字符串对象,每次调用方法后,原字符串都会发生变化
- StringBuffer和StringBuilder并没有重写equals方法,所以可变字符串的值是否相同时,调用的是equals中原始的==判断。如果要判断两个可变字符串的值是否相同时,需要将其转换为String后调用equals判断
可变字符串相关面试题
比较String,StringBuilder和StringBuffer的区别
- 相同点
- 都可以表示字符串。都提供了一些操作字符串的方法。
- 都有相同的方法,如charAt()、indexOf等。
- 都是被final修饰的类,不能被继承。
- 不同点
- String定义的字符串是一个常量,可变字符串定义的字符串是一个变量。
- String类中的方法,调用后,不会改变原本字符串的值;可变字符串类中的方法,调用后,会改变原本字符串的值。
- StringBuilder是非线程安全的可变字符串类,StringBuffer是线程安全的可变字符串类,其中的方法被synchronized修饰。
- 总结
在频繁操作同一个字符串时,一定要使用可变字符串StringBuidler或StringBuffer类的对象,不能使用String类的对象。
方法调用的传值问题
只有参数是引用类型(类,数组,接口),在方法中是直接操作该参数,才会对实际参数造成影响。(引用类型如果在方法中再创建新对象则不会对实际参数造成影响)
与八大原始类型对应的包装类
包装类 | 原始类型 |
---|---|
Byte | byte |
Short | short |
Integer | int |
Long | long |
Float | float |
Double | double |
Character | char |
Boolean | boolean |
异常
方法
try-catch-finally
原理
先执行try中的代码,当出现异常,与后续catch中的异常类型进行匹配,如果匹配到对应的
类型或异常父类型,则执行大括号中的代码,最终一定执行finally中的内容
注意点
- try、catch、finally都不能单独使用,try需要配合catch或finally或catch和finally一起使用。
- 执行try中的内容时,当某行代码抛出异常,不再执行try中该行代码后续的内容。
- 无论try中的代码是否会抛出异常,finally中的代码一定会执行。
- 如果代码会抛出多个异常,可以使用多个catch进行捕获,需要将异常子类放在最前,异常父类放在最后。
- 如果代码会抛出多个异常,可以使用多个catch进行捕获,需要将异常子类放在最前,异常父类放在最后。
- 在try中定义的内容,无法在try之外的地方使用。
- try中如果有return,不影响finally的执行,finally优先于return执行。
try-catch-finally相关面试题
final、finally、finalize的区别
- final是一个修饰符,被final修饰的属性称为常量,方法不能被重写,类不能被继承。
- finally是try-catch-finally结构中的关键字,在无论是否抛出异常,都会执行的代码块。
- finalize是Object类中的方法,finalize()在某个对象被回收前调用的方法。
throws关键字
throw和throws的区别
- throws表示用于声明方法有可能出现的异常。使用时写在方法的小括号之后。
- throw用于手动抛出异常对象。使用时,写在方法体中,常用于满足某种情况时,强制中断程序用法:throw异常对象。
数组和集合的区别
数组的特点
- 数组中保存的元素都是有序的。可以通过下标快速访问。
- 数组中保存的数据都是同一种类型。
- 数组的长度在定义后,无法改变。
- 数组无法获取其中保存的元素实际数量。
集合的特点
- 能保存一组数据,可以有序也可以无序。
- 集合的容量可变。
- 集合中可以保存不同类型的数据。
- 可以获取集合中保存的元素实际数量。
泛型
定义
一种规范,常用于限制集合中元素的类型,省去遍历元素时判断是否为对应类型和转型的过程。
用法
在定义集合遍历时,在类后面写上<引用数据类型>
集合类或接口<引用数据类型> 集合变量名= new集合实现类();List<String> list =new ArrayList(); //当前集合只能保存String类型的元素 list.add("sdfsdf"); //list.add(123);//无法添加 List<Integer> list2=newArrayList(); list2.add(123);
集合家族
Collection接口
常用方法
方法名 | 返回值 | 作用 |
---|---|---|
add(Object obj) | boolean | 将元素添加到集合中 |
size() | int | 获取集合中的元素数量 |
isEmpty() | boolean | 判断集合是否为空 |
clear() | clear() | 清空集合 |
contains(Object obj) | boolean | 判断集合中是否存在指定元素 |
remove(Object obj) | boolean | 移除集合中的指定元素 |
toArray() | Object[] | 将集合转换为数组 |
iterator() | iterator | 获取集合的迭代器对象,用于遍历集合 |
lterable接口为什么不是集合的根接口
原因
- lterable被称为迭代器,是用于遍历集合元素的一个工具接口。
Collections集合工具类
与Collection的区别
- Collection是集合的根接口,定义了集合操作元素的方法。
- Collections是集合的工具类,定义了集合操作元素的静态方法。
常用方法
方法名 | 作用 |
---|---|
Collections.shuffle(List list) | 打乱集合中的元素顺序(洗牌) |
Collections.swap(List list,int a,int b) | 交换集合中的元素位置,参数为集合和两个索引 |
Collections.replaceAll(List list,Object obj,Object obj) | 替换集合中的旧元素为新元素 |
Collections.sort(List list) | 对集合中的元素进行排序,根据集合中元素的类的compareTo()方法排序 |
Collections.reverse(List[] list) | 翻转集合中的元素 |
list接口
特点
- 有序可重复
- 可以根据索引进行删除、获取元素等
实现类接口-ArrayList接口
特点
- 采用数组实现的集合。
- 可以通过索引访问元素,可以改变集合大小,如果要在其中插入和删除元素时,会影响后续元素。
- 该集合中保存的都是引用类型。
- 该集合查询效率高,中途增加和删除元素效率低。
构造方法
常用构造方法名· | 说明 |
---|---|
ArrayList() | 创建一个Object类型的空数组。在调用添加方法后,该数组大小为10 |
ArrayList(int initialCapacity) | 创建一个指定容量的Object数组,如果参数为负,会抛出IllegalArgumentException异常 |
常用方法实现了Collection接口和List接口。
实现类接口-LinkedList接口
特点
- 采用双向链表实现的集合。
- 集合中保存的每个元素也称为节点,除首尾节点外,其余节点都保存了自己的信息外,还保存了其前一个和后一个节点的地址。
- 如果在双向链表的数据结构中插入和删除操作节点时,不会影响其他节点的位置。如添加时新节点时,只需要重写定义新节点的前后节点位置即可。
- 如果要查询某个节点时,需要从头结点或尾结点开始一步步得到目标节点的位置。
- 双向链表在中间插入和删除的效率高,随机读取的效率低。
构造方法
常用构造方法名 | 说明 |
---|---|
LinkedList() | 创建一个空链表 |
常用方法不仅实现了Collection接口和List接口,还实现了Deque接口的方法。
set接口
特点
- 无序不重复。
- 允许保存null,没有索引。
- 没有自己定义的方法,都是继承于Collection接口中的方法。
哈希表(set集合实现原理)
- 哈希表,也称为散列表,是—种数据结构,能更快地访问数据。
- 要保存的数据称为原始值,这个原始值通过一个函数得到一个新的数据,这个函数称为哈希函数,这个新数据称为哈希码,哈希码和原始值之间有一个映射关系,这个关系称为哈希映射,可以构造一张映射表,这个表称为哈希表。在哈希表中,可以通过哈希码快速地访问对应的原始值。
- 哈希冲突,多出现在哈希函数有一定的几率让多个原始值得到相同的哈希码(哈希码一致,实际值不同)。
- 值得注意的是,如果两个对象的hashCode不同,这两个对象一定不同。如果两个对象的hashCode相同,这两个对象不—定相同。(此处就可能出现哈希冲突)
- 解决哈希冲突,就要使用"拉链法",将重复的这个哈希码所在的位置向链表—样进行延伸。
实现接口-HashSet接口
特点
- 采用哈希表实现。
- 元素不能重复,无序保存,允许保存null。
- 本质是一个HashMap对象。
- 使用HashSet集合时,通常要重写实体类中的equals和hashcode方法。
常用方法没有属于自定义的方法,都是重写了父接口Set和Collection中的方法。(注意没有与索引相关的方法。)
添加数据原理
- 如果两个的hashCode相同且equals结果为true,视为同一个对象,不能添加。
- 每次向集合中添加元素时,先判断该元素的hashCode是否存在:
如果不存在,视为不同对象,直接添加;
如果存在,再判断equals方法的结果(如果false,视为不同对象,可以添加;如果true,视为同一对象,不能添加)
equals方法和hashCode的关系
- 如果没有重写equals,默认是Object中使用==判断,如果结果为true,说明是同一个对象,hashCode一定相同。
- hashCode不同,说明不是同一个对象,没有重写equals,说明使用Object中equals的==判断,结果为false。
- 如果两个对象的hashCode相同,equals方法的比较结果为:可能是true(同一地址),可能是false(哈希冲突)。
实现接口-TreeSet接口
特点
- 特殊的Set实现类,数据可以有序保存,可以重复,不能添加null。
- 采用红黑树(自平衡二叉树实现的集合。
- 只能添加同一种类型的对象且该类实现了Comparable接口。
- compareTo()方法的返回值决定了能否添加新元素和新元素的位置。
- 添加的元素可以自动排序。
构造方法
常用构造方法名 | 说明 |
---|---|
TreeSet() | 创建一个空集合,实际上是创建了一个TreeMap对象。 |
使用场景
- 如果要保存的元素需要对其排序,使用该集合。
- 保存在其中的元素必须要实现Comparable接口,且重写compareTo()方法,自定义排序规则。
集合和数组之间的转换
集合转换为数组
使用Collection接口中的toArray()方法。
Object[] obj = 集合对象.toArray(); List<Integer> list = new ArrayList(); list.add(123); list.add(63); list.add(3); Integer[] nums = (Integer[])list.toArray();
数组转换为集合
遍历数组的同时添加到集合中。
//一个数组对象 int[] nums = {11,2,66,3,6,21}; //定义集合对象 List list=new ArrayList(); //遍历数组的同时添加到集合中 for(int i : nums){list.add(i); }
一组数据转换为集合
使用Arrays工具类中的asList(一组数据)方法。
List<String> strings = Arrays.asList("XX","aa","qq","xx");
Map接口
特点
- Map成为映射,数据以键值对的形式保存。
- 键称为Key,值称为Value,键不能重复,键允许出现一个null作为键,值无限制。
- 键和值都是引用类型。
常用方法
方法名 | 作用 |
---|---|
size() | 得到键值对的数量 |
clear() | 清空所有键值对 |
put(Object key,Object value) | 向集合中添加一组键值对 |
get(Object key) | 在集合中根据键得到对应的值 |
remove(Object key)/remove(Object key,Object key) | 根据键或键值对移除 |
keyset() | 获取键的集合 |
values() | 获取值的集合 |
containsKey(Object key) | 判断是否存在某个键 |
containsValue(Object value) | 判断是否存在某个值 |
entrySet() | 得到集合中的所有键值对 |
实现类-HashMap接口
构造方法
常用构造方法名 | 说明 |
---|---|
HashMap() | 创建一个空的映射集合,默认大小为16,加载因子为0.75 |
常用方法继承自Map。
遍历集合中元素的方式
遍历List集合
ArrayList<String> nameList = newArrayList();
nameList.add("Tom");
nameList.add("Jerry");
nameList.add("LiHua");
nameList.add("Danny");
方式一:普通for循环
for(int i=0;i<nameList.size();i++){//从0遍历到size()
String name=nameList.get(i);//通过get(intindex)获取指定索引的元素
System.out.println(name);
}
方式二:增强for循环
for(String name : nameList){System.out.println(name);
}
方式三:迭代器
//Collection类型的集合对象.iterator(),获取迭代器
Iterator<String> iterator=nameList.iterator();
//iterator.hasNext()判断集合中是否还有下一个元素
//iterator.next();获取下一个元素
while(iterator.hasNext()){String name=iterator.next();System.out.println(name);
}
遍历Set集合
Set hs = new HashSet();
hs.add(123);
hs.add("hello");
hs.add(null);
hs.add(987);
方式一:增强for循环
for(Object o:hs){System.out.println(o);
}
方式二:迭代器
Iterator<Object> it=hs.iterator();
while(it.hasNext()){System.out.println(it.next());
}
遍历Map集合
Map<Integer,User> hm= new HashMap<>();
User u1=newUser("admin","123123");
User u2=newUser("tom","123123");
User u3=newUser("jerry","123123");
hm.put(1001,u1);
hm.put(1002,u2);
hm.put(1003,u3);
方式一:根据键得到对应的值
for(Integer id : hm.keySet()){//遍历键//根据键得到对应的值System.out.println(id+"\t"+hm.get(id).getUsername());
}
方式二:遍历键值对
//得到当前hashmap对象中的所有键值对的集合
Set<Map.Entry<Integer,User>> entries = hm.entrySet();
//遍历键值对
for(Map.Entry<Integer,User> entry:entries){System.out.println(entry);
}
数组管理系统 VS 集合管理系统
使用数组完成-ATM系统
User类(封装的用户类)
/*** 客户类* 属性:* 账户* 密码* 余额* 上次登录时间* 账户状态(0:表示正常;1:表示冻结)* 账单* 方法:* 有参构造方法* getter和setter* toString(重写)* equals(重写)*/
public class User {private String name;private String password;private double balance;private String lastLogin;private int status;public Bill[] bills;public User(String name, String password) {this.name = name;this.password = password;this.lastLogin = null;this.balance = 10000;this.status = 0;this.bills = new Bill[100];}//用户实体输出格式重写@Overridepublic String toString() {String s = status==0?"正常":"冻结";return name + "\t" +"\t" + password +"\t" + lastLogin+"\t" + s +"\t";}//用户信息比较重写@Overridepublic boolean equals(Object obj) {if (this==obj){return true;}if (obj instanceof User){User user = (User) obj;if (name.equals(user.getName())&&password.equals(user.getPassword())){return true;}}return false;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}public String getLastLogin() {return lastLogin;}public void setLastLogin(String lastLogin) {this.lastLogin = lastLogin;}public int getStatus() {return status;}public void setStatus(int status) {this.status = status;}}
Bill类(封装的账单类)
/*** 账单类* 属性:* 操作类型(取款,存款)* 金额* 时间* 方法* setter和getter* 重写toString()*/
public class Bill {public String operate;public String money;public String pointTime;public Bill(String operate, String money, String pointTime) {this.operate = operate;this.money = money;this.pointTime = pointTime;}@Overridepublic String toString() {return operate + "\t" +money + "\t" +pointTime;}
}
自定义异常类-MoneyException类(取多抛出的异常)
public class MoneyException extends Exception{public MoneyException(String message) {super(message);}
}
自定义异常类-passwordException类(密码多次错误抛出的异常)
public class passwordException extends RuntimeException{public passwordException(String message) {super(message);}
}
ATM类(主要方法集合)(注意整个过程返回对象之后,就会一直直接操作此对象,所有不需要再更新信息回数组中,此处本人犯过错,需谨慎!!!)
/*** ATM提款机类* 属性:* 客户数组* 方法:* 客户登录* 客户注册* 查看余额* 提款* 存款* 冻结账户* 注销账户* 更新账户信息* 查看账户信息* 记录个人账单* 获得当前格式化的时间* 查看账单*/
public class ATM {User[] users;public ATM() {users = new User[100];}//客户登录public User login(User user){if (user==null){return null;}for (int i = 0; i < users.length; i++) {if (users[i]!=null&&users[i].equals(user)){return users[i];}}return null;}//客户注册public boolean register(User user){if (user==null){return false;}for (int i = 0; i < users.length; i++) {if (users[i]!=null&&users[i].equals(user)){return false;}if (users[i]==null){users[i] = user;return true;}}return false;}//查看余额public void viewBalance(User user){System.out.println("当前用户"+user.getName()+"的余额为:"+user.getBalance()+"元");}//提款public boolean withdrawal(User user,Double money){if (user.getBalance()-money<=0){return false;}else {Bill bill = new Bill("提款",money.toString(),getTime());if (updateBill(user,bill)){System.out.println("记录账单成功!");}else {System.out.println("记录账单失败!");}user.setBalance(user.getBalance()-money);return true;}}//存款public boolean deposit(User user,Double money){Bill bill = new Bill("存款",money.toString(),getTime());if (updateBill(user,bill)){System.out.println("记录账单成功!");}else {System.out.println("记录账单失败!");}user.setBalance(user.getBalance()+money);return true;}//冻结账户public boolean freeze(User user){user.setStatus(1);return true;}//解锁账户public boolean unfreeze(User user){user.setStatus(0);return true;}//根据账号名查找账号public User getUserByName(User user){for (int i = 0; i < users.length; i++) {if (users[i]!=null&&users[i].getName().equals(user.getName())){return users[i];}}return null;}//注销账户public boolean cancelUser(User user){for (int i = 0; i < users.length; i++) {if (users[i]!=null&&users[i].equals(user)){users[i]=null;return true;}}return false;}//更新账户信息//public boolean updateUserInfo(User user){// for (int i = 0; i < users.length; i++) {// if (users[i]!=null&&users[i].equals(user)){// users[i] = user;// return true;// }// }// return false;//}//查看本用户信息public void viewUserInfo(User user){System.out.println(user);}//记录账单public boolean updateBill(User user,Bill bill){for (int i = 0; i < user.bills.length; i++) {if (user.bills[i]==null){user.bills[i]= bill;return true;}}return false;}//获取格式化当前的时间public String getTime() {String mb = "YYYY/MM/dd hh:mm:ss EE";SimpleDateFormat sdf = new SimpleDateFormat(mb);return sdf.format(new Date());}//查看账单public void viewBill(User user){for (Bill bill : user.bills) {if (bill!=null) {System.out.println(bill);}}}}
Main类(主入口)
/*** 主入口* 方法:* 首页* 菜单* 验证用户状态情况* 重新更新密码失败次数* 解锁冻结的账户*/
public class Main {static Scanner sc = new Scanner(System.in);static ATM atm = new ATM();static User user;static int loginCount;public static void main(String[] args) throws MoneyException {System.out.println("----------------欢迎来到穷人如何变成富人银行系统------------------");updateCount();menu();}//银行首页public static void menu() throws MoneyException {System.out.println("---------------首页----------------");System.out.println("1.登录账户");System.out.println("2.注册账户");System.out.println("3.解锁冻结的账户");System.out.println("0.退出系统");System.out.print("请输入您的选项:");int menuChoose = sc.nextInt();switch (menuChoose){case 1:loginMenu();break;case 2:registerMenu();menu();break;case 3:unfreezeUser();break;case 0:System.exit(0);break;}}//首页调用登录的方法public static void loginMenu() throws MoneyException {System.out.println("-------登录界面------");System.out.println("【温馨提示:注册过的用户,密码不能错三次,否则将冻结账户】");System.out.print("请输入账户名:");String loginName = sc.next();System.out.print("请输入密码:");String loginPassword = sc.next();user = new User(loginName,loginPassword);//调用验证用户登录状态的方法if (loginAndStatus(user)){User userSys = atm.login(user);//更新密码错误次数updateCount();//登录成功进入菜单System.out.println("恭喜登录成功!请进入主菜单...");enterSys(userSys);};//登录失败重新加载首页menu();}//首页调用注册的方法public static void registerMenu() throws MoneyException {System.out.println("-------注册界面------");System.out.print("请输入新账户名:");String regName = sc.next();System.out.print("请输入密码:");String regPassword = sc.next();user = new User(regName,regPassword);if (atm.register(user)){System.out.println("新账号注册成功!");menu();}else {System.out.println("账户已存在!请重新输入...");registerMenu();}}//进入系统public static void enterSys(User user) throws MoneyException {System.out.println("--------------主菜单---------------");System.out.println("1.查看余额");System.out.println("2.取款");System.out.println("3.存款");System.out.println("4.查看用户信息");System.out.println("5.查看用户账单");System.out.println("6.注销账户");System.out.println("7.切换账户");System.out.println("0.返回首页");System.out.print("请输入选项:");int enterSysChoose = sc.nextInt();switch (enterSysChoose){case 1:System.out.println("-------查看余额界面------");//调用查看余额的方法atm.viewBalance(user);enterSys(user);break;case 2:System.out.println("-------取款界面------");System.out.print("请输入取款额:");double moneyOut = sc.nextDouble();try {//单次取款额高于最高限额5000元,抛出异常,并不会取出成功if (moneyOut>5000){throw new MoneyException("单次取款额需在5000元以内,请重试...");}}catch (MoneyException e){System.out.println(e);enterSys(user);}try {//调用取款的方法,如果余额不足,将强制退出登录if (atm.withdrawal(user,moneyOut)){System.out.println("已取款:"+moneyOut);} else {throw new MoneyException("余额不足,没钱还敢取这么多,将退出当前账号...");}}catch (MoneyException e){System.out.println(e);menu();}enterSys(user);break;case 3:System.out.println("-------取款界面------");System.out.print("请输入存款额:");double moneyIn = sc.nextDouble();if (atm.deposit(user,moneyIn)){System.out.println("已存款"+moneyIn);}else {System.out.println("存款失败!");}enterSys(user);break;case 4:System.out.println("-------查看用户信息界面------");System.out.println("用户名\t密码\t上次登录时间\t状态");//调用查看账户信息的方法atm.viewUserInfo(user);enterSys(user);break;case 5:System.out.println("-------查看用户账单界面------");System.out.println("操作\t金额\t时间");//调用查看账单的方法atm.viewBill(user);enterSys(user);break;case 6://当注销当前账户后,此账户上所有信息将清空,同时返回首页页面System.out.println("-------注销账户界面------");if (atm.cancelUser(user)){System.out.println("注销用户成功!");}else {System.out.println("注销用户失败!请重试...");enterSys(user);}menu();break;case 7://调用首页登录的方法loginMenu();break;case 0://当用户退出登录时,更新到数据库系统(也就是用户数组,减少资源消耗)//if (atm.updateUserInfo(user)){user.setLastLogin(atm.getTime());System.out.println("正在返回首页请稍等...");//}else {// System.out.println("更新用户信息失败!请重试...");// enterSys(user);//}menu();break;}}//验证登录和账户状态public static boolean loginAndStatus(User user){if (atm.login(user)==null){User userFindByName = atm.getUserByName(user);try {if (userFindByName!=null) {loginCount++;//尝试密码次数加1System.out.println("第"+loginCount+"次尝试失败!");//如果密码尝试次数到达3次将冻结账号,不能登录if (loginCount == 3) {atm.freeze(userFindByName);throw new passwordException("登录错误次数达到3次!将冻结此用户,请注意...");}}}catch (passwordException e){System.out.println(e);}System.out.println("非常抱歉!账户信息错误,请重新输入...");return false;} else if(atm.login(user).getStatus()==1){//判断存在的用户是否为冻结状态,如果是将不能登录。System.out.println("非常遗憾!您的账户现在处于冻结状态,暂时无法登陆!");return false;}return true;}//重新更新密码失败次数(使用不同账户名登录时,要重置)public static void updateCount(){loginCount = 0;}//解锁冻结的账户public static void unfreezeUser() throws MoneyException {System.out.println("----------账户解锁界面------------");System.out.println("请输入要解锁的账号名:");String userName = sc.next();System.out.println("请输入密码:");String password = sc.next();user = new User(userName,password);User userSys = atm.login(user);if (userSys!=null){Random rd = new Random();StringBuilder sbd1 = new StringBuilder();//生成数字随机数int num1=(rd.nextInt(10));int num2=(rd.nextInt(10));//生成小写字母随机数int engLower = rd.nextInt(36)+65;//生成大写字母随机数int engBig = rd.nextInt(39)+97;//转换成字符char eL =(char)engLower;char eB =(char)engBig;//生成验证码数组集合String[] yz = {eL+"",eB+"",num1+"",num2+""};int chooseNum = 0;for (int i = 0; i < 4; i++) {chooseNum = rd.nextInt(4);sbd1.append(yz[chooseNum]);}System.out.println("验证码:"+sbd1);String yzSys=sbd1.toString();System.out.print("请输入验证码:");String yzUser=sc.next();if (yzSys.equals(yzUser)){System.out.println("验证码输入正确!");System.out.println("正在解锁冻结的账户,请稍等...");//调用解锁账户方法if (atm.unfreeze(userSys)){System.out.println("解锁成功!请登录...");}}else {System.out.println("验证码错误!请重试...");//验证码错误重新进入验证unfreezeUser();}}else {System.out.println("此账户未注册,请重新输入账户...");unfreezeUser();}}}
使用(List集合)ArrayList实现-新闻管理系统
News类(封装的新闻)
/***新闻类* 属性:* 标题* 内容* 编辑* 发布时间* 方法:* 有参构造* equals* toString* getter和setter* 日期生成器*/
public class News {private String title;private String content;private String editor;private String releaseTime;public News(String title, String content, String editor) {this.title = title;this.content = content;this.editor = editor;this.releaseTime = getTime();}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;News news = (News) o;return Objects.equals(title, news.title) && Objects.equals(content, news.content) && Objects.equals(editor, news.editor) && Objects.equals(releaseTime, news.releaseTime);}@Overridepublic String toString() {return "News{" +"title='" + title + '\'' +", content='" + content + '\'' +", editor='" + editor + '\'' +", releaseTime='" + releaseTime + '\'' +'}';}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getEditor() {return editor;}public void setEditor(String editor) {this.editor = editor;}public String getReleaseTime() {return releaseTime;}public void setReleaseTime(String releaseTime) {this.releaseTime = releaseTime;}//日期生成器public String getTime(){String fp = "YYYY年MM月dd HH:ss:mm E";SimpleDateFormat sdf = new SimpleDateFormat(fp);String time = sdf.format(new Date());return time;}
}
NewsAgency类(方法集合体) (注意此处根据自动生成的有顺序的编号进行标识和索引,所以不用遍历整个集合,直接根据索引值判断即可)
public class NewsAgency {ArrayList<News> newsArrayList = new ArrayList();//添加新闻public boolean addNews(News news){boolean result = newsArrayList.add(news);return result;}//删除新闻public boolean deleteNewsById(int id){if (newsArrayList.get(id-1)!=null){newsArrayList.remove(id-1);return true;}return false;}//查看所有新闻public void showAllNews(){System.out.println("编号\t标题");int id = 0;String title ="";for (int i = 0; i < newsArrayList.size(); i++) {id = i + 1;title = newsArrayList.get(i).getTitle();System.out.println(id+"\t"+title);}}//查看指定新闻的内容(这里最好返回对象给其它方法使用)public boolean showNewsById(int id){//for (int i = 0; i < newsArrayList.size(); i++) {// if ((i+1)==id) {// String content = newsArrayList.get(i).getContent();// System.out.println(id+"编号新闻的内容"+content);// return true;// }else {// System.out.println(id+"编号新闻不存在!");// }//}if (newsArrayList.get(id-1)!=null){String content = newsArrayList.get(id-1).getContent();String title = newsArrayList.get(id-1).getTitle();String editor = newsArrayList.get(id-1).getEditor();String releaseTime = newsArrayList.get(id-1).getTime();System.out.println("编号\t标题\t内容\t编辑\t发布时间");System.out.println(id+"\t"+title+"\t"+content+"\t"+editor+"\t"+releaseTime);return true;}return false;}//修改指定新闻的内容public boolean updateNewsById(int id,String newContent){if (newsArrayList.get(id-1)!=null){newsArrayList.get(id-1).setContent(newContent);return true;}return false;}}
Main类(主入口)
public class Main {static Scanner sc = new Scanner(System.in);NewsAgency newsAgency = new NewsAgency();News news;public static void main(String[] args) {Main main = new Main();main.menu();}public void menu(){System.out.println("------------首页-------------");System.out.println("1.添加新闻");System.out.println("2.删除新闻");System.out.println("3.查看新闻");System.out.println("4.查看详情");System.out.println("5.修改新闻");System.out.println("0.退出新闻管理系统");System.out.print("请输入选项:");int menuChoose = sc.nextInt();switch (menuChoose){case 1:addN();break;case 2:deleteNById();break;case 3:viewAll();break;case 4:viewDetail();break;case 5:updateNById();break;case 0:System.exit(0);break;}menu();}//添加public void addN(){System.out.println("---------添加新闻界面----------");System.out.print("请输入新闻标题:");sc.nextLine();String title = sc.nextLine();System.out.print("请输入新闻内容:");String content = sc.nextLine();System.out.print("请输入新闻编辑人:");String editor = sc.nextLine();news = new News(title,content,editor);if (newsAgency.addNews(news)){System.out.println("添加成功!");menu();}else {System.out.println("添加失败!请重试...");addN();}}//删除public void deleteNById(){System.out.println("---------删除新闻界面---------");System.out.print("请输入待删除新闻的编号:");int id = sc.nextInt();if (newsAgency.deleteNewsById(id)){System.out.println("删除编号为"+id+"的新闻成功!");menu();}else {System.out.println("删除编号为"+id+"的新闻失败!请重试...");deleteNById();}}//查看所有public void viewAll(){System.out.println("---------查看所有新闻界面---------");newsAgency.showAllNews();}//查看详情public void viewDetail() {System.out.println("---------查看新闻详情界面---------");System.out.print("请输入待查新闻的编号:");int id = sc.nextInt();if (newsAgency.showNewsById(id)){menu();}else {System.out.println("查询失败!请重试界面...");viewDetail();}}//修改public void updateNById(){System.out.println("---------修改新闻---------");System.out.print("请输入待修改新闻的编号:");int id = sc.nextInt();System.out.println("请输入更新的新闻内容:");String content = sc.next();if (newsAgency.updateNewsById(id,content)){System.out.println("修改编号为"+id+"的新闻成功!");menu();}else {System.out.println("修改失败!请重试...");updateNById();}}
}
使用(Map集合)HashMap实现-用户管理系统
User类(封装的用户)
public class User {private String id;private String name;private String password;private String date;public User(String name, String password) {this.name = name;this.password = password;this.date = new SimpleDateFormat("YYYY年MM月dd日 HH:mm:ss E").format(new Date());}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", password='" + password + '\'' +", date='" + date + '\'' +'}';}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getDate() {return date;}public void setDate(String date) {this.date = date;}
}
Main类(主方法)
/*** 使用HashMap添加并查看*/
public class Main {HashMap users = new HashMap();static Main main = new Main();static Scanner sc = new Scanner(System.in);public static void main(String[] args) {main.menu();}public void menu(){System.out.println("--------------用户信息管理---------------");System.out.println("1.添加新用户");System.out.println("2.查看所有用户");System.out.println("0.退出系统");int choose = sc.nextInt();switch (choose){case 1:main.addUsers();break;case 2:main.showUsers();break;case 0:System.exit(0);}}public void addUsers(){System.out.println("----------添加新用户------------");System.out.println("请输入编号:");String id = sc.next();System.out.println("请输入用户名:");String name = sc.next();System.out.println("请输入密码:");String password = sc.next();if (users.containsKey(id)){System.out.println("编号已存在!请重新输入...");addUsers();}//实例化新用户User user = new User( name, password);//使用HashMap集合添加用户users.put(id,user);System.out.println("已添加新用户...");menu();}public void showUsers(){System.out.println("----------所有用户信息------------");System.out.println("编号\t用户详情");for (Object o : users.keySet()) {System.out.print(o+"\t");System.out.println(users.get(o));}menu();}}
文件类File
定义
- Java中的File类,表示本地硬盘中的文件(文件和目录)的一个类。
- 通过这个类创建的对象,可以操作对应的文件。
构造方法
常用构造方法名 | 说明 |
---|---|
File(String pathName) | 根据文件的完整路径创建File对象 |
File(String parent,String child) | 根据文件的父目录路径和自身路径创建File对象 |
File(File parent,String child) | 根据文件的父目录对应的File对象和自身路径创建File对象 |
常用方法
方法名 | 说明 |
---|---|
exists() | 判断文件是否存在 |
isFile() | 判断是否为文件 |
isDirectory() | 判断是否为目录 |
getName() | 获取文件名 |
getPath() | 获取文件相对路径 |
getAbsolutePath() | 获取文件绝对路径 |
length() | 获取文件所占字节 |
delete() | 删除文件或空目录 |
mkdir() | 创建目录 |
list() | 获取某个目录下的第一层子文件的名称的数组 |
IO
I:Input输入
O:Output输出
流Stream
特点
- 在Java中,流用于表示计算机硬盘与内存之间传输数据的通道。
- 将内存中的数据存入到硬盘中,称为写write,也称为输出Output。
- 将硬盘中的数据存入到内存中,称为读read,也称为输入Input。
流的分类
字节输入流InputStream
FileInpuStream、ObjectInputStream
字节输出流OutputStream
FileOutputStream、ObjectOutputStream
字符输入流Reader
FileReader、BufferedReader、OutputStreamWriter
字符输出流Writer
FileWriter、BufferedWriter、InputStreamReader
按方向分类
- 输入流:InputStream、Reader(将硬盘中的数据读取到内存中)
- 输出流:OutputStream、Writer(将内存中的数据写入到硬盘中)
按类型分
- 字节流:InputStream、OutputStream(读写非文本类型文件。如图片、音视频、其他文件等。)
- 字符流:Reader、Writer(读写纯文本类型文件。如txt、md等)
应用场景
- 如要将硬盘中某个txt文件中的内容读取到程序中,使用Reader。
- 如要将硬盘中的某个图片读取到程序中,使用InputStream。
- 如要将程序中的文本写入到硬盘中为txt类型文件时,使用Writer。
- 如要将程序中的数据写入到硬盘中为非文本文件时,使用OutputStream。
流的四个父类的特点
- 这四个父类都是在java.io包下,都是抽象类,不能直接创建其对象,使用其子类创建对象。
- 这四个父类中都定义了close()方法,用于关闭流对象,释放资源。
- 输入流(InputStream和Reader)都有read()方法读取数据到内存中,输出流都有write()方法写入数据到硬盘中。
- 输出流(OutputStream和Writer)都有flush()方法,用于将流中的数据冲刷到硬盘中
在使用输出流对象时,一定要调用flush()或close()方法后,才能真正将数据写入到硬盘中。- 所有的流中,以Stream结尾,都是字节流,数据以字节传输;以Reader或Writer结尾的,都是字符流,数据以字符传输。
- 读取硬盘中的数据,使用输入流,读取的文件必须存在;将数据写入到硬盘中,使用输出流,文件可以不存在,但父目录必须存在。
- 读入或写入文本时,使用字符流;读取或写入非文本时,使用字节流。
使用FileInputStream和FileOutputStream实现-单文件的复制
public class CopyFile{
public static void main(String[]args) throws IOException{
//定义原文件和目标文件
File source= new File("F:\\FileInputStream和FileOutputStream.mp4");
File target=new File("F:\\copy.mp4");
//定义文件字节输入流,用于读取原文件
File InputStream fis=new FileInputStream(source);
//定义文件字节输出流,用于写入文件
FileOutputStream fos=new FileOutputStream(target);
/*
//调用无参的read()方法,表示读取一个字节,返回读取到的字节
int read=fis.read();
//如果能读取到内容
while(read>-1){
//将读取到的内容写入到文件中
fos.write(read);
//继续读取
read=fis.read();
}
*/
//定义一个字节数组,大小为8MB
byte[] bytes=new byte[1024*1024*8];
//按字节数组读取,返回读取到的字节数量
int count=fis.read(bytes);
//循环读取写入
while(count>-1){
//将读取的字节数组写入到文件中
//fos.write(bytes);//如果调用该方法,最后一次会多写入上一次残留的数据
fos.write(bytes,0,count);//如果调用该方法,实际读取到了多少字节就写入多少
count=fis.read(bytes);
}
fis.close();
fos.close();if(target.exists()){System.out.println("复制成功");}}
}
文件夹的复制
public class CopyDirectory{
public static void main(String[]args){
/*Filesource=newFile("F:\\流的基本概念.mp4");
Filetarget=newFile("F:\\copy.mp4");
copyFile(source,target);*/
Filesource=newFile("F:\\笔记");
Filetarget=newFile("F:\\笔记副本");
/*
*sourceF:\221001\笔记
*targetF:\221001\笔记副本
*1.调用copyDir方法,判断发现source是一个文件夹,创建目标文件夹
target:“F:\221001\笔记副本”
*2.遍历source,如其中有xxx.md文件,即child
*此时的source是F:\221001\笔记\xxx.md,即child
*此时的target是F:\221001\笔记副本\xxx.md,用File(Fileparent,Stringchild)
构造方法表示这个目标文件
*所以创建File newTarget=newFile(target,child.getName())
*
**/
copyDir(source,target);
}
/*
*定义复制文件夹的方法
**/
public static void copyDir(File source,File target){
//如果是文件,调用单文件复制的方法
if(source.isFile()){
copyFile(source,target);
}else{//如果是文件夹
//创建要复制的目标文件夹
target.mkdir();
//展开原文件夹
for(File child:source.listFiles()){
//定义复制后的新目标文件
//如source为F:\221001\笔记\day1.md时,递归调用的target为F:\221001\笔
记副本\day1.md
File newTarget=newFile(target,child.getName());//这里使用
File(File parent,String child)构造方法创建target对象
//递归调用的原文件依然是当前遍历出来的子文件,目标文件就是最终复制的
F:\221001\笔记副本\day1.md
copyDir(child,newTarget);
}
}
}
/*
*定义单文件复制的方法
**/
public static void copyFile(Filesource,Filetarget){
FileInputStream fis=null;
FileOutputStream fos=null;
try{
//创建用于输入输出的流对象
fis=new FileInputStream(source);
fos=new FileOutputStream(target);
//定义字节数组
byte[]bytes=new byte[1024*1024*8];
//按数组读取
int count=fis.read(bytes);
while(count!=-1){
fos.write(bytes,0,count);
count=fis.read(bytes);
}
}catch(FileNotFoundExceptione){
System.out.println("文件不存在"+e);
}catch(IOExceptione){
System.out.println("读写异常"+e);
}finally{
try{
if(fis!=null){
fis.close();
}
if(fos!=null){
fos.close();
}
}catch(IOExceptione){
System.out.println("关闭流对象异常"+e);
}
}
}
}
序列化和反序列化应用
Person类(实现Serializable接口)
/*
*如果希望该类的对象能序列化,写入对象到本地,必须要实现Serializable接口
*Serializable接口中没有任何方法,是一个标记接口,表示该类的对象可以被序列化
**/
public class Person implements Serializable{private String name;private intage;private String sex;//省略getter/setter和toString()
}
Main类
public class Test1{
public static void main(String[]args)throwsIOException,ClassNotFoundException{
Person p1=newPerson("王海",22,"男");
Person p2=newPerson("赵敏",24,"女");
Person p3=newPerson("刘涛",21,"女");
ArrayList<Person> list=newArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
//创建OutStream的实现类,设置写入的文件路径
OutputStream os=new FileOutputStream("F:\\person.p");
//创建对象输出字节流,参数为OutStream类型
ObjectOutputStream oos=new ObjectOutputStream(os);
//调用writeObject(Objectobj)方法,将对象写入到硬盘中(序列化)
oos.writeObject(list);
oos.close();
//创建对象输入字节流,将上一步保存的文件进行反序列化
ObjectInputStream ois=new ObjectInputStream(newFileInputStream("F:\\person.p"));
//使用readObject()方法,将写入的文件进行读取(反序列化)
ArrayList<Person> pList=(ArrayList<Person>)ois.readObject();
for(Person person:pList){System.out.println(person);
}
ois.close();
}
}
网络编程
InetAddress类
定义
表示IP对象的一个类。
Socket类和ServerSocket类
相同点
都属于Socket(套接字)对象,表示网络中的某个端点。
不同点
Socket指普通端。
ServerSocket指服务器端。
使用套接字对象实现两个端点(Socket和ServerSocket)之间发送文件
服务端
/*
*使用套接字对象,实现客户端向服务端发送文件
*
*定义服务端套接字对象
**/
public class Server{
public static void main(String[]args)throws IOException{
//以本机创建服务端套接字对象
ServerSocket server=new ServerSocket(8899,100,
InetAddress.getLocalHost());
//等待客户端连接,返回连接的客户端套接字对象
Socket client=server.accept();
//定义要将读取到的数据写入到本地的文件字节输出流对象
FileOutputStream fos=new FileOutputStream("上传文件.md");
//获取客户端与服务端的输入流对象,读取发送的数据
InputStream is=client.getInputStream();
//定义读取的字节数组
byte[]bytes=new byte[1024*1024*8];
int count=is.read(bytes);
while(count!=-1){
//将读取到的数据写入到本地
fos.write(bytes,0,count);
count=is.read(bytes);
}
fos.close();
is.close();
}
}
客户端
/*
*定义客户端套接字对象
**/
public class Client{
public static void main(String[]args)throws IOException{
//创建客户端套接字对象,连接指定的服务端套接字对象
Socket client=new Socket("192.168.31.39",8899);
//获取客户端与服务端的输出流对象
OutputStream os=client.getOutputStream();
//成功连接后,将某个文件发送给服务端
//定义要发送的文件对象
File file = new File("F:\\a.md");
//读取要发送的文件
FileInputStream fis=new FileInputStream(file);
//定义字节数组
byte[] bytes=new byte[1024*1024*8];
//循环读取要发送的文件
int count=fis.read(bytes);
while(count!=-1){
//将读取到的数据写入到客户端套接字与服务端套接字的通道中
os.write(bytes,0,count);
count=fis.read(bytes);
}
fis.close();
os.close();
}
}
进程和线程
进程Process
进程就是操作系统中执行的程序。一个程序就是一个执行的进程实体。每个运行中的进程,都有属于它独立的内存空间,各个进程互不影响。
线程Thread
- 线程是一个进程中的执行单元,一个进程中可以有多个线程。
- 多个线程,可以访问同一个进程中的资源。
- 每个线程都有一个独立的栈空间,这些线程所在的栈空间位于同一个进程空间中。
多线程
- 如果一个进程中,同时在执行着多个线程,就称为多线程。
- 多线程可以提高程序执行效率。如多个窗口卖票,可以加快卖票的效率。
- 其实每个执行的Java程序,都是多线程执行,main方法称为主线程,还有gc线程(守护线程)在同时运行。
并行和并发
并行
各个进程同时执行。
并发
多个线程同时执行,称为并发。
同步和异步
同步
所有的任务排队执行。
异步
在执行任务A的同时,执行任务B。
Java中的线程Thread类
获取当前正在运行的线程对象
Thread ct = Thread.cuurentThread();
创建一个线程对象(构造方法)
常用构造方法名 | 说明 |
---|---|
Thread() | 创建一个默认的线程对象 |
Thread(String name) | 创建一个指定名称的线程对象 |
Thread(Runnable target) | 将一个Runnable对象包装为线程对象 |
Thread(Runnable target,Stringname) | 将一个Runnable对象包装为线程对象同时设置线程名 |
常用方法
方法名 | 作用 |
---|---|
start() | 让线程进入就绪状态 |
run() | 线程获得执行权时执行的方法(线程要做的事情) |
Thread.sleep(long m) | 设置当前线程休眠m毫秒 |
Thread.currentThread() | 获取当前执行的线程对象 |
实现多线程
方式一:继承Thread类
- 创建一个类,继承Thread类。
- 重写Thread类中的run()方法。
- 创建自定义的线程子类对象后,调用start()方法。
方式二:实现Runnable接口(建议使用)
自定义一个类,实现Runnable接口。
重写run()方法,将多线程要执行的内容写在该方法中。
创建Runnable接口的实现类对象。
使用构造方法Thread(Runnable target)或Thread(Runnable target,String name)将上一步创建。
的Runnable实现类对象包装为Thread对象。
方式三:使用匿名内部类
使用匿名内部类充当Runnable接口的实现类。
生命周期
新生状态
- 当线程对象被创建后,就进入了新生状态。
就绪状态
- 当某个线程对象调用了start()方法后,就进入了就绪状态。
- 在这个状态下,线程对象不会做任何事情,只在等他CPU调度。
运行状态
- 当某个线程对象得到CPU时间片(CPU执行这个线程的机会所给的时间),则进入运行状态,开始执行run()方法。
- 不会等待run()方法执行完毕,只会在指定的时间内尽可能地执行run()方法。只要调用玩run()方法后,就会再进入就绪状态。
阻塞状态
- 如果某个线程遇到了sleep()方法或wait()方法时,就会进入阻塞状态。
- sleep()方法会在指定时间后,让线程重新就绪。
- wait()方法只有在被调用notify()或notifyAll()方法唤醒后才能重新就绪。
终止状态
- 当某个线程的run()方法中的所有内容都执行完,就会进入终止状态,意味着该线程的使命已经完成。
守护线程
特点
- 如果将一个线程设置setDeamon(true),表示该线程为守护线程。
- 守护线程会随着其他非守护线程终止而终止。
多线程访问同一个资源
可能出现的问题
多个线程对同一对象异步进行操作,导致结果不能达到预期。
出现问题的原因
- 由于线程调用start()方法后,就进入就绪状态。如果获得了CPU时间片,就开始调用run()方法,调用run()方法后,就会再次进入就绪状态,不会等待run()方法执行完毕,所以在线程A执行run()方法的时候,线程B也开始执行了,这样就会出现数据共享的问题。
- 因为现在所有的线程都是异步(同时)执行。
如何解决
让线程同步(排队)执行即可。这样一来,某个线程执行run()方法的时候,让其他线程等待run()方法的内容执行完毕。
synchronized关键字
可以修饰方法或代码块
修饰方法
写在方法的返回值之前,这时该方法就称为同步方法。
public synchronized void fun(){//会排队执行的代码 }
修饰代码块
写在一个独立的{}前,这时该段内容称为同步代码块。
synchronized(要同步的对象或this){//会排队执行的代码 }
原理
- 每个对象默认都有一把"锁",当某个线程运行到被synchronized修饰的方法时,该对象就会拥有这把锁,在拥有锁的过程中,其他线程不能同时访问该方法,只有等待其结束后,才会释放这把锁。
- 使用synchronized修饰后的锁称为"悲观锁"。
- 方法被synchronized修饰后,称为同步方法,就会让原本多线程变成了单线程(异步变为同步)。
死锁
场景
当多个线程同时获取不同资源时,恰好获得的资源都是对方下一步执行所需的资源,但是双方都在等待资源发放,导致多个线程一直不结束运行,也不继续执行下去。
解决方式
方式一
- 让两个线程获取资源的顺序保持一致。
方式二
- 让两个线程在获取资源A和B之前,再获取第三个资源,对第三个资源使用synchronized进行同步,这样某个线程在获取第三个资源后,将后续内容执行完毕,其他线程才能开始执行。
多线程相关面试题
实现多线程的方式?
- 继承Thread类
- 实现Runnable接口后,包装为Thread对象
- 匿名内部类
为什么说StringBuilder或ArrayList、HashMap是非线程安全的?
因为多个线程对这些类型创建的对象进行操作不能达到预期的结果。
个人总结
在这个阶段中能明显感觉到了知识点的增多,如果不能多次进行回顾,很可能会混淆和忘弃。所以这是很值得重视的一点。同时相关面试题也增多了,这也需要自己去理解和记忆。整理刚学知识点更要学会和以前类似的知识点做区分,更深层次的理解其原理。在做作业时的问题也有必要做一个笔记整理,这样能大大避免再次出错。每个程序员天生不是敲代码,但是却是天生有学习逻辑思维的能手,一次又一次的错误,我相信这会成为每一个人前行路上的“灯塔”!加油!!!
华清远见-重庆中心-JAVA高级阶段技术总结/个人总结相关推荐
- 华清远见-重庆中心-JAVA高级阶段技术总结/知识点梳理/面试题解析
String字符串 String是一个类,属于数据类型中的引用类型. Java中一切使用""引起来的内容,都是这个类的实例,称为字符串对象. 字符串在定义后,值不可改变,是一个常量 ...
- 华清远见-重庆中心-JAVA高级阶段技术总结/知识点梳理/个人总结
文章目录 String字符串 String类使用时注意 如何创建字符串对象 1.使用""赋值创建! 2.通过构造方法创建 不同方式创建字符串的过程 使用""赋值 ...
- 华清远见-重庆中心-JAVA高级阶段技术总结
目录 String字符串 可变字符串 StringBuilder类 StringBuffer类 构造方法 普通方法 比较String.StringBuilder和StringBuffer的区别 Run ...
- 华清远见-重庆中心-Java高级阶段技术总结:
Java容器分为Collection和Map两大类,其下又有很多子类. Collection还有父接口Iterable,但Iterable接口不算严格意义上的集合的根接口.它称为迭代器,是用于遍历集合 ...
- 华清远见—重庆中心——JAVA高级阶段知识点梳理
华清远见-重庆中心--JAVA高级阶段知识点梳理 String字符串 String是一个类,属于数据类型中的引用类型.Java中所有使用""引起来的内容都是属于这个类的实例,称为字 ...
- 华清远见-重庆中心-JAVA基础阶段技术总结
系列文章目录 第一章 华清远见--重庆中心-JAVA基础阶段技术总结 第二章 文章目录 系列文章目录 文章目录 前言 一.关于java 1.发展历程 2.编程开发 3.java架构 4.java的特点 ...
- 华清远见-重庆中心-JAVA面向对象阶段技术总结
华清远见-重庆中心-JAVA面向对象阶段技术总结 面向对象和面向过程的编程思想 面向对象(OOP) 通过创建(new)对象,赋予对象对应的行为和特征,让这些对象相互配合来解决问题 面向过程(POP) ...
- 华清远见-重庆中心-JAVA基础阶段技术总结/知识点梳理/个人总结/关于JAVA技术的解析(看法)/面试题解析
Java基础知识 概述 什么是java:1.是边编译边解释的面向对象的编程语言. 2.java语言是1995年发布的,发布语言公司是:Sun 3.下载安装Java时从Oracle官网下载,最好下载LT ...
- 华清远见重庆中心—JAVA基础阶段技术总结/个人总结
文章目录 Java基础知识 概述 Java程序运行过程 Java的分类 Java为什么流行 Java能够跨平台运行的原理 JDK和JRE 第一个Java程序 Java程序的运行流程 集成开发环境IDE ...
最新文章
- python sqlalchemy中文手册-基于Python的SQLAlchemy的操作
- run loop 测试
- xampp默认配置拿shell
- 图的基本操作及其相关应用
- Socket-Client通信
- 图数据库之Pregel
- Generator函数自执行
- java 远程debug 内网_java jdb remote debug - java远程调试
- 【报告分享】5G时代新型基础设施建设白皮书.pdf
- GAOT工具箱设置交叉概率和变异概率
- arcgis中将地理坐标转换为投影坐标 / 经纬度坐标转换
- 网络热门知识点,Linux内核——网络协议栈基本知识
- 朴素贝叶斯,拉普拉斯平滑
- 12个开放平台大家一起来玩
- 如何使用Vegas进行调音?
- php GD绘图技术(实战)
- matlab画PV曲线程序,Matlab模拟包络线形成:Envolope程序
- 什么是ICP经营许可证?
- el-select被嵌套太多层导致选不中
- 计算机网络面试题总结之一
热门文章
- Ubuntu下的一款Dock工具-AWN
- 微耕门禁、Monitor监控等设备的二次开发
- springboot基础(10):给springboot项目添加网页
- 无线串口服务器连接plc,4G/5G无线PLC远程控制
- 天线的E面和H面,theta和phi理解
- python语音识别whisper
- 达梦数据库“-2685:试图在blob或者clob列上排序或比较“
- AI CC 2018安装破解详细教程
- 五、广义逆矩阵–求解线性方程组
- 学校计算机实训室标语,职业学校实训室标语