华清远见-重庆中心-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")

 说明:

  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类的对象。

方法调用的传值问题

只有参数是引用类型(类,数组,接口),在方法中是直接操作该参数,才会对实际参数造成影响。(引用类型如果在方法中再创建新对象则不会对实际参数造成影响)

与八大原始类型对应的包装类

包装类原始类型
Bytebyte
Shortshort
Integerint
Longlong
Floatfloat
Doubledouble
Characterchar
Booleanboolean

异常

方法

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];
    }

    //用户实体输出格式重写
    @Override
    public String toString() {
        String s = status==0?"正常":"冻结";
        return name + "\t" +
                "\t" + password +
                "\t" + lastLogin+
                "\t" + s +
                "\t";
    }

    //用户信息比较重写
    @Override
    public 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;
    }

    @Override
    public 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++;//尝试密码次数加1
                    System.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();
    }


    @Override
    public 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);
    }

    @Override
    public 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());
    }

    @Override
    public 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是非线程安全的?

因为多个线程对这些类型创建的对象进行操作不能达到预期的结果。

个人总结

        在这个阶段中能明显感觉到了知识点的增多,如果不能多次进行回顾,很可能会混淆和忘弃。所以这是很值得重视的一点。同时相关面试题也增多了,这也需要自己去理解和记忆。整理刚学知识点更要学会和以前类似的知识点做区分,更深层次的理解其原理。在做作业时的问题也有必要做一个笔记整理,这样能大大避免再次出错。每个程序员天生不是敲代码,但是却是天生有学习逻辑思维的能手,一次又一次的错误,我相信这会成为每一个人前行路上的“灯塔”!加油!!!

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值