目录

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")

 说明:

  1. 创建StringBuilder对象
  2. 在字符串常量池中创建"Ja"
  3. 在字符串常量池中创建"va"
  4. 创建String对象,保存"va"的地址
  5. 调用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高级阶段技术总结/个人总结相关推荐

  1. 华清远见-重庆中心-JAVA高级阶段技术总结/知识点梳理/面试题解析

    String字符串 String是一个类,属于数据类型中的引用类型. Java中一切使用""引起来的内容,都是这个类的实例,称为字符串对象. 字符串在定义后,值不可改变,是一个常量 ...

  2. 华清远见-重庆中心-JAVA高级阶段技术总结/知识点梳理/个人总结

    文章目录 String字符串 String类使用时注意 如何创建字符串对象 1.使用""赋值创建! 2.通过构造方法创建 不同方式创建字符串的过程 使用""赋值 ...

  3. 华清远见-重庆中心-JAVA高级阶段技术总结

    目录 String字符串 可变字符串 StringBuilder类 StringBuffer类 构造方法 普通方法 比较String.StringBuilder和StringBuffer的区别 Run ...

  4. 华清远见-重庆中心-Java高级阶段技术总结:

    Java容器分为Collection和Map两大类,其下又有很多子类. Collection还有父接口Iterable,但Iterable接口不算严格意义上的集合的根接口.它称为迭代器,是用于遍历集合 ...

  5. 华清远见—重庆中心——JAVA高级阶段知识点梳理

    华清远见-重庆中心--JAVA高级阶段知识点梳理 String字符串 String是一个类,属于数据类型中的引用类型.Java中所有使用""引起来的内容都是属于这个类的实例,称为字 ...

  6. 华清远见-重庆中心-JAVA基础阶段技术总结

    系列文章目录 第一章 华清远见--重庆中心-JAVA基础阶段技术总结 第二章 文章目录 系列文章目录 文章目录 前言 一.关于java 1.发展历程 2.编程开发 3.java架构 4.java的特点 ...

  7. 华清远见-重庆中心-JAVA面向对象阶段技术总结

    华清远见-重庆中心-JAVA面向对象阶段技术总结 面向对象和面向过程的编程思想 面向对象(OOP) 通过创建(new)对象,赋予对象对应的行为和特征,让这些对象相互配合来解决问题 面向过程(POP) ...

  8. 华清远见-重庆中心-JAVA基础阶段技术总结/知识点梳理/个人总结/关于JAVA技术的解析(看法)/面试题解析

    Java基础知识 概述 什么是java:1.是边编译边解释的面向对象的编程语言. 2.java语言是1995年发布的,发布语言公司是:Sun 3.下载安装Java时从Oracle官网下载,最好下载LT ...

  9. 华清远见重庆中心—JAVA基础阶段技术总结/个人总结

    文章目录 Java基础知识 概述 Java程序运行过程 Java的分类 Java为什么流行 Java能够跨平台运行的原理 JDK和JRE 第一个Java程序 Java程序的运行流程 集成开发环境IDE ...

最新文章

  1. python sqlalchemy中文手册-基于Python的SQLAlchemy的操作
  2. run loop 测试
  3. xampp默认配置拿shell
  4. 图的基本操作及其相关应用
  5. Socket-Client通信
  6. 图数据库之Pregel
  7. Generator函数自执行
  8. java 远程debug 内网_java jdb remote debug - java远程调试
  9. 【报告分享】5G时代新型基础设施建设白皮书.pdf
  10. GAOT工具箱设置交叉概率和变异概率
  11. arcgis中将地理坐标转换为投影坐标 / 经纬度坐标转换
  12. 网络热门知识点,Linux内核——网络协议栈基本知识
  13. 朴素贝叶斯,拉普拉斯平滑
  14. 12个开放平台大家一起来玩
  15. 如何使用Vegas进行调音?
  16. php GD绘图技术(实战)
  17. matlab画PV曲线程序,Matlab模拟包络线形成:Envolope程序
  18. 什么是ICP经营许可证?
  19. el-select被嵌套太多层导致选不中
  20. 计算机网络面试题总结之一

热门文章

  1. Ubuntu下的一款Dock工具-AWN
  2. 微耕门禁、Monitor监控等设备的二次开发
  3. springboot基础(10):给springboot项目添加网页
  4. 无线串口服务器连接plc,4G/5G无线PLC远程控制
  5. 天线的E面和H面,theta和phi理解
  6. python语音识别whisper
  7. 达梦数据库“-2685:试图在blob或者clob列上排序或比较“
  8. AI CC 2018安装破解详细教程
  9. 五、广义逆矩阵–求解线性方程组
  10. 学校计算机实训室标语,职业学校实训室标语