Skip to content

String ​

String API ​

统计这段绕口令有多少个以p开头的单词 ​

"peter piper picked a peck of pickled peppers"

tips:

  • split

  • startsWith

    java
    	public static void main(String[] args) {
            int count = 0;
            String str = "peter piper picked a peck of pickled peppers";
            String[] ss = str.split(" ");
            for (String s : ss) {
                if(s.startsWith("p")){
                    count++;
                }
            }
            System.out.println(count);
        }

大小写 ​

把字符串"lengendary"中字符 改成间隔大写小写模式,即 "LeNgEnDaRy"

java
    public static void main(String[] args) {
        String str = "lengendary";//LeNgEnDaRy
        String res ="";
        int len = str.length();
        for (int i = 0; i < len; i++) {
            if(i % 2 == 0){
                res += (char)(str.charAt(i) - ('a' - 'A'));
            } else res += str.charAt(i);
        }
        System.out.println(res);
    }
java
    public static void main(String[] args) {
        String str = "lengendary";//LeNgEnDaRy
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            if(i % 2 == 0){
                //变成大写
                res.append(Character.toUpperCase(str.charAt(i)));
            } else res.append(str.charAt(i));
        }
        System.out.println(res);
    }

大串中小串出现的次数 &#8203;

在字串”woaijavawozhenaijavawozhendeaijavawozhendehenaijavaxinbuxinwoaijavagun” 中java出现了5次, 统计次数

tips:

  • indexOf

  • 仅考虑本题举出的字符串,无需复杂考虑

    java
        public static void main(String[] args) {
            String str = "woaijavawozhenaijavawozhendeaijavawozhendehenaijavaxinbuxinwoaijavagun";
            int count = 0;
            int index = str.indexOf("java");
            while(index != -1){//只要能找到就一直往后找
                count++;
                index = str.indexOf("java",index + 1);
            }
            System.out.println(count);
        }

综合练习 &#8203;

穷举匹配密码 &#8203;

  1. 生成一个长度是3的随机数字字符串,把这个字符串作为当做密码
  2. 使用穷举法生成长度是3个字符串,匹配上述生成的密码,将列举到的可能的密码打印,直到匹配成功结束.

tips:

  • Random

  • equals

  • String 数组

    java
        public static void main(String[] args) {
            matchPwd(getPwd());
        }
        //生成三位数的随机密码
        public static String getPwd(){
            Random rd = new Random();
            String pwd = "";
            for (int i = 0; i < 3; i++) {
                int pw1 = rd.nextInt(10);
                pwd += pw1;
            }
            System.out.println("随机密码是" + pwd);
            return pwd;
        }
        //使用穷举法匹配字符串
        public static void matchPwd(String pwd) {
            for (int i = 0; i < 1000; i++) {
                String match = String.format("%03d",i);//输出为3位数,用0填充
                if(pwd.equals(match)){
                    System.out.println(match + "匹配到了");
                    break;
                }
            }
        }

排序() &#8203;

创建一个长度是8的字符串数组
使用8个长度是5的随机字符串初始化这个长度为8的字符串数组
对这个数组进行排序,按照每个字符串的首字母排序(无视大小写)

注1: 不能使用Arrays.sort() 要自己写
注2: 无视大小写,即 Axxxx 和 axxxxx 没有先后顺序
举例:
排序前
[UxoEl, If3js, 9SSwC, czHuA, lZLBF, IhynX, VgjrY, dJAIW]
排序后
[9SSwC, czHuA, dJAIW, If3js, IhynX, lZLBF, UxoEl, VgjrY]

tips:

  • 冒泡排序
  • Random
  • 数组
  • String API
java
public class Demo {
    public static void main(String[] args) {
        String[] strs = new String[8];
        for (int i = 0; i < 8; i++) 
            strs[i] = getRandomStr(5);
        System.out.println("排序前:"+Arrays.toString(strs));
        mySort(strs);
        System.out.println("排序后:"+Arrays.toString(strs));
    }
    private static void mySort(String[] strs) {
        //冒泡
        int len = strs.length;
        for (int i = 0; i < len - 1; i++) {//冒泡多少趟
            for (int j = 0; j < (len - 1 - i); j++) {//每趟比较多少次
                //取出首字母,全部转小写,进行比较
                //对数字没有影响
                char c = Character.toLowerCase(strs[j].charAt(0));
                char c1 = Character.toLowerCase(strs[j+1].charAt(0));
                if(c1 < c){//交换
                    String tmp = strs[j];strs[j] = strs[j+1];strs[j+1] = tmp;
                }
            }
        }
    }
    //得到长度是len的一个随机字符串
    public static String getRandomStr(int len){
        StringBuilder res = new StringBuilder();
        //制作一个随机字符池,把所有可能用到的随机字符放在池子里
        List<Character> pool = new ArrayList<>();
        //大写字母,小写字母,数字
        for (int i = 'A'; i <= 'Z'; i++) pool.add((char)i);
        for (int i = 'a'; i <= 'z'; i++) pool.add((char)i);
        for (int i = '0'; i <= '9'; i++) pool.add((char)i);
        Random rd = new Random();
        //分len次,分别随机取池子里的字符
        for (int i = 0; i < len; i++) {
            char random = pool.get(rd.nextInt(pool.size()));
            res.append(random);
        }
        return res.toString();
    }
}

统计重复字符串() &#8203;

创建一个长度是100的字符串数组
使用长度是2的随机字符填充该字符串数组
统计这个字符串数组里重复的字符串有多少种(忽略大小写)

例如
20个字符串一行 共5行
01 0S 1M 1q 01 2R 2S 3d 3k 3x 43 4A 4R 5T 7C 7U 7e 7k 80 88
8p AD AG AY BF BO Db E2 Eg El GF Gm HC HO IB If J0 JY Jv KW
L0 LT MA NL NL NU PY Qx Rr Ry SB So TH Tu UR VD WR Xe Xw Xx
Y3 YL Yd Yk a3 aL bW bq cI cv cv dg eL fL gA gL gh h4 hm j7
jp kN m1 mS o6 pQ qV rP rf s7 sA um vE 01 vb w6 wB xP yK yQ

El发生了重复 重复了2次
NL发生了重复 重复了2次
Yk发生了重复 重复了2次
cv发生了重复 重复了2次

tips:

  • String 数组
  • Random
  • String API
java
    private static void getRepeat(){
        //生成长度100的随机字符串数组
        String[] str = new String[100];
        for (int i = 0; i < 100; i++) {
            str[i] = getRandomStr(2);
        }
        //统计这个字符串数组中重复的字符串有多少种
        Map<String, Integer> mp = new HashMap<>();
        for (String s : str) {
            //忽略大小写比较都转成大写
            String ups = s.toUpperCase();
            if (mp.keySet().contains(ups)) {
                mp.put(ups, mp.get(ups) + 1);
            } else mp.put(ups, 1);
        }
        for (Map.Entry<String, Integer> e : mp.entrySet()) {
            if(e.getValue()>1){
                System.out.printf("<%s : %d>\n",e.getKey(),e.getValue());
            }
        }
    }
    public static String getRandomStr(int len){
        StringBuilder res = new StringBuilder();
        //制作一个随机字符池,把所有可能用到的随机字符放在池子里
        List<Character> pool = new ArrayList<>();
        //大写字母,小写字母,数字
        for (int i = 'A'; i <= 'Z'; i++) pool.add((char)i);
        for (int i = 'a'; i <= 'z'; i++) pool.add((char)i);
        for (int i = '0'; i <= '9'; i++) pool.add((char)i);
        Random rd = new Random();
        //分len次,分别随机取池子里的字符
        for (int i = 0; i < len; i++) {
            char random = pool.get(rd.nextInt(pool.size()));
            res.append(random);
        }
        return res.toString();
    }
java
	 private static void getRepeat1(){
        //生成长度100的随机字符串数组, 每个字符串由2个字符组成,20个一行输出
        int len = 100, col = 20,c = 2;
        String[] str = new String[len];
        for (int i = 0; i < len; i++) {
            str[i] = getRandomStr(c);
            System.out.print(str[i] + "\t");
            if((i + 1) % col == 0) System.out.println();
        }
        //统计这个字符串数组中重复的字符串有多少种  遍历方法类似选择排序
        for (int i = 0; i < len - 1; i++) {
            int count = 1;//count必须等于1
            for (int j = i+1; j < len; j++) {
                if(str[i] == null) continue;
                if(str[i].equalsIgnoreCase(str[j])){
                    count++;
                    str[j] = null;//找到相同的置空,下次跳过
                }
            }
            if(count > 1)
                System.out.println(str[i]+" 重复:"+count);
        }
    }

自然排序 &#8203;

编写一个Cat类

成员变量:

int age,

String name,

double price

按以下要求进行自然排序:

直接实现Comparable接口,按照年龄的从小到大对Cat对象数组进行排序。

基于Comparator自定义比较器接口,用Lambda表达式对Cat对象数组进行排序。

  1. 按照price从高到低进行排序
  2. 按照name的长短从短到长进行排序
  3. 综合age,name,price进行排序,要求用Lambda表达式的方法引用语法。排序的规则是先按照age从小到大排序,age相同比较name长短,按照name的长度从短到长排序,name长度也相等,比较price,price按照从高到低排序。

如果你懒于自己搞容器数据,可以使用以下数据:

java
// Cat类有三参构造器(age, name, price)
Cat c = new Cat(1, "小黄", 100);
Cat c1 = new Cat(2, "小黑", 200);
Cat c2 = new Cat(2, "貉", 2000);
Cat c3 = new Cat(2, "马云的猫", 10000);
Cat c4 = new Cat(2, "云哥", 8000);
Cat c5 = new Cat(3, "吉祥物", 800);
Cat c6 = new Cat(2, "阿修罗", 9000);
Cat c7 = new Cat(2, "云堇", 300);
Cat c8 = new Cat(2, "咪咪", 400);
Cat c9 = new Cat(8, "养乐多", 500);
Cat[] cats = {c, c1, c2, c3, c4, c5, c6, c7, c8, c9};
1,实现Comparable接口,按照年龄的从小到大对Cat数组进行自然排序
2,利用Comparator接口,用 匿名内部类和lambda分别对Cat数组进行自然排序
    1,按照price的大到小排序
    2,name的长短排序,name越长对象越大
3,综合age,name,price进行排序,要求用lambda表达式指向一个方法。
 排序的规则是age越小对象越小,age相同比较name长短,name越短对象越小,name长度也相等,比较price,price越大对象越小

 实现Comparable接口,需要修改源码,需要类去实现Object1.compareTo(Object2)方法
      可以看成o1 - o2
 实现Comparator接口,传入比较器对象,这种方式更加灵活
      需要实现方法compare(Object1,Object2)仍然看成o1 - o2

 比如排序的条件是按照分数从高到低:
      假如o1是自己,我的分高越小,方法应该得到一个负数,所以应该是别人减我
java
public class Demo2 {
    public static void main(String[] args) {
        Cat c = new Cat(1, "小黄", 100);
        Cat c1 = new Cat(2, "小黑", 200);
        Cat c2 = new Cat(2, "貉", 2000);
        Cat c3 = new Cat(2, "马云的猫", 10000);
        Cat c4 = new Cat(2, "云哥", 8000);
        Cat c5 = new Cat(3, "吉祥物", 800);
        Cat c6 = new Cat(2, "阿修罗", 9000);
        Cat c7 = new Cat(2, "云堇", 300);
        Cat c8 = new Cat(2, "咪咪", 400);
        Cat c9 = new Cat(8, "养乐多", 500);
        Cat[] cats = {c, c1, c2, c3, c4, c5, c6, c7, c8, c9};
        //1.实现Comparable接口,按照年龄的从小到大对Cat数组进行自然排序
        Arrays.sort(cats);
        System.out.println(Arrays.toString(cats));
        //2.利用Comparator接口,用 lambda, 按照price从高到低进行排序
        Arrays.sort(cats, (o1,o2)->Double.compare(o2.price,o1.price));
        System.out.println(Arrays.toString(cats));
        //3.利用Comparator接口,按照name的长短, 从短到长进行排序
        Arrays.sort(cats, (o1,o2)->o1.name.length() - o2.name.length());
        System.out.println(Arrays.toString(cats));
        //4.综合age,name,price进行排序,要求用Lambda表达式的方法引用语法。
        // 排序的规则是先按照age从小到大排序,age相同比较name长短,
        // 按照name的长度从短到长排序,name长度也相等,比较price,price按照从高到低排序。
        Arrays.sort(cats, Cat::msort);
        System.out.println(Arrays.toString(cats));
    }
}
class Cat implements Comparable<Cat>{
    int age;
    String name;
    double price;

    public Cat(int age, String name, double price) {
        this.age = age;
        this.name = name;
        this.price = price;
    }
    public static int msort(Cat o1,Cat o2){
        if(o1.age == o2.age){
            if(o1.name.length() ==o2.name.length()){
                return Double.compare(o2.price, o1.price);
            }
            return o1.name.length() - o2.name.length();
        }
        return o1.age - o2.age;
    }
    @Override
    public String toString() {
        return "Cat{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }

    @Override
    public int compareTo(Cat o) {
        return this.age - o.age;
    }
}

可变长字符串 &#8203;

判断一个字符串是否是对称字符串,例如"abc"不是对称字符串,"aba"、"abba"、"aaa"、"mnanm"是对称字符串

判断一个字符串是否是对称的字符串,我只需要把

第一个和最后一个比较

第二个和倒数第二个比较

...

tips:

  • 可以借助StringBuffer
  • reverse
java
    private static boolean isSymmetryStr(String s) {
        String r = new StringBuffer(s).reverse().toString();
        return s.equals(r);
    }

日期练习 &#8203;

制作一个工具类,算一下你来到这个世界多少天?

tips:

  • Date
  • SimpleDateFormat
java
    public static void getBirth() throws ParseException {
        SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println("输入出生年月日,按照格式yyyy-MM-dd:");
        Scanner sc = new Scanner(System.in);
        String birth = sc.nextLine();
        //转化时间戳
        long time = fm.parse(birth).getTime();
        //现在时间戳-出生时间戳
        long distance = System.currentTimeMillis() - time;
        //毫秒时间戳转天数
        System.out.println("你来到这个世界 " + (distance / 1000 / 60 / 60 / 24) + "天了");
    }

异常 &#8203;

异常处理 &#8203;

Scanner进行键盘录入时,非常容易产生异常,那么就有必要进行处理
我们在进行键盘录入字符串操作时,有时需要把一个字符串转换成int类型。但如果输入的字符串并不是一个数字时,
显然是不能转换成功的,而是会抛出一个异常。
   要求:
      处理该异常,让程序不会报错终止,而是让用户重新输入,直到输入正确的数字为止

tips:

  • try-catch
java
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入数字:");
        int num = 0;
        while(true){
            try {
                num = Integer.parseInt(sc.nextLine());
            } catch (NumberFormatException e) {
                System.out.println("输入不合法,重新输入");
                continue;
            }
            break;
        }
        System.out.println("输入的数字是:"+num);
    }

自定义异常 &#8203;

分别自定义编译时异常和运行时异常,并在方法中抛出异常,main方法中调用该方法,并处理相应的异常

tips:

  • RuntimeException

  • Exception

    java

File &#8203;

File API &#8203;

需求:

递归删除目录与文件

给大家发一个目录,里面是多级目录与文件,使用递归的方式,将该目录里面的文件,目录全删除了.(目录为空才可以删)
注意: 不要放在C盘

tips:

  • 递归

  • delete

  • isDirectory

  • listFiles

  • getAbsolutePath

    java
        public static void walkDirs(File f){
            File[] files = f.listFiles();
            for (File file : files) {
                if(file.isFile()){
                    if(file.delete()){
                        System.out.println(file.getAbsolutePath()+"成功删除");
                    }else System.out.println("删除失败");
                }else{
                    walkDirs(file);
                    if(file.delete()){
                        System.out.println(file.getAbsolutePath()+" 成功删除");
                    }else System.out.println("删除失败");
                }
            }
        }

文件过滤 &#8203;

自己建立文件夹 ,放一些文件, 文件夹

仅针对这三个条件提供规则方法,具体实现自己去做
条件一:仅留下文件夹名包括a的文件夹
条件二:仅留下.txt结尾的文本文件
条件三:仅留下以a开头的文件

tips:

  • listFiles

  • FileFiter

  • lambda/匿名内部类

    java
        public static void testFilter(File f){
            //得到当前文件夹下,所有符合条件的目标文件夹数组
            File[] files = f.listFiles(pathname-> {
                String fileName = pathname.getName();
                return (pathname.isDirectory() && fileName.contains("a"))
                        || (pathname.isFile() &&
                        ((fileName.startsWith("a")) || (fileName.endsWith(".txt"))));
            });
            //打印符合要求的目标
            for (File target : files) {
                System.out.println(target.getAbsolutePath());
            }
            //如果是文件夹,继续递归执行
            File[] dir = f.listFiles(pathname -> pathname.isDirectory());
            for (File d : dir) {
                testFilter(d);
            }
        }

IO &#8203;

字节流 &#8203;

键盘输入并保存1 &#8203;

分别键盘输入文件名 和 文件内容,并按照文件名保存相应的内容

tips:

  • FileOutputStream

  • Scanner

  • 异常处理

    java
        public static void main(String[] args) throws IOException {
            //创建输出流
            System.out.println("输入要保存的文件名:");
            Scanner sc = new Scanner(System.in);
            FileOutputStream os = new FileOutputStream(sc.nextLine());
            //写入
            System.out.println("输入内容:");
            byte[] bs = sc.nextLine().getBytes();
            os.write(bs);
            System.out.println("完成");
            //释放资源
            os.close();
        }

键盘输入并保存2 &#8203;

键盘输入文件名,文件内容,按照输入的文件名,文件内容保存。 要求输入内容的时候可以多次追加写入,以一个约定字符串表示结束输出内容, 比如当输入end时表示终止内容输入。

tips:

  • FileOutputStream

  • Scanner

  • 异常处理

  • equals

    java
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            System.out.println("输入文件名:");
            String fname = sc.nextLine();
            StringBuilder sb = new StringBuilder();
            String content = "";
            System.out.println("输入内容:");
            while("end".equals(content = sc.nextLine())==false){
                sb.append(content);
            }
            System.out.println("将要写入文件的内容:"+sb.toString());
            try (FileOutputStream fo = new FileOutputStream(fname, true)){
                fo.write(sb.toString().getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

字符流 &#8203;

统计各种字符 &#8203;

从磁盘上读取一个文本文件(如某个java源代码),
分别统计出文件中英文字母、空格、数字字符的个数。(注意文本文件中的数字指的是数字字符!!!)

tips:

  • FileReader
  • ASCII 或Character
  • 异常处理
java
    public static void main(String[] args) {//
        try(BufferedReader fd = new BufferedReader(
                new FileReader("E:\\idea\\oop\\src\\com\\testFile\\Demo.java"));) {
            int c = -1;
            int letterCount = 0,spaceCount = 0,digitCount = 0;
            //逐个字符读取,进行判断
            while((c = fd.read())!=-1){
                if(Character.isDigit(c))
                    digitCount++;
                else if(Character.isAlphabetic(c))
                    letterCount++;
                else if (c == ' ')
                    spaceCount++;
            }
            System.out.println("letterCount = " + letterCount);
            System.out.println("digitCount = " + digitCount);
            System.out.println("spaceCount = " + spaceCount);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

文本按行逆序 &#8203;

利用字符流去读取一个文本文件,每次读取一行,并将这行数据逆序,写入新的文本文件当中

tips:

  • BufferedReader

  • BufferedWriter/ FileWriter

  • String API/StringBuffer

  • 异常处理

    java
        public static void main(String[] args) {
            try(BufferedReader br = new BufferedReader(new FileReader("test.txt"));
                BufferedWriter bw = new BufferedWriter(new FileWriter("testWrite.txt"))) {
                String str = "";
                while((str = br.readLine())!=null){
                    StringBuilder sb = new StringBuilder(str);
                    bw.write(sb.reverse().toString());
                    bw.newLine();
                }
                System.out.println("完成");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

综合 &#8203;

单词分类(动词,名词) &#8203;

有这样的一个words数组,数组中每个字符串的格式为“词性:单词”

String[] words = {"verb:eat","verb:drink","verb:sleep","verb:play","noun:rice","noun:meat","noun:hand","noun:hair"};

根据单词性质动词verb全部存入verb.txt文件中

根据单词性质名词noun全部存入noun.txt文件中

tips:

  • FileOutputStream
  • String API
  • 异常处理
java
    public static void main(String[] args) {
        String[] words = {"verb:eat","verb:drink","verb:sleep","verb:play","noun:rice","noun:meat","noun:hand","noun:hair"};
        try(FileOutputStream fv = new FileOutputStream("verb.txt");
            FileOutputStream fn = new FileOutputStream("noun.txt");) {
            for (String w : words) {
                if(w.contains("verb")){
                    //截取冒号之后的内容 存入文件
                    String word = w.split(":")[1]+System.lineSeparator();
                    fv.write(word.getBytes());
                }else if(w.contains("noun")){
                    //截取:之后的内容 存入文件
                    String word = w.split(":")[1]+System.lineSeparator();
                    fn.write(word.getBytes());
                }
            }
            System.out.println("完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

统计奇数偶数 &#8203;

读取一个文件,这个文件中有随机的一些数字字符,统计这些数字有几个偶数,几个奇数,并且追加写入到该文件末尾。
例如:
a.txt文件:
   3241256364789629090126581212515
   奇数:xx个
   偶数:xx个

tips:

  • FileInputStream

  • FileOutputStream

  • 取余数

  • 异常处理

    java
        public static void main(String[] args) {
            try(FileReader fr = new FileReader("test.txt");
                FileWriter fw = new FileWriter("test.txt",true);
            ) {
                int c = 0;
                int evenCount = 0, oddCount = 0;
                while ((c = fr.read()) != -1) {
                    int num =Character.getNumericValue(c);
                    if(num % 2 == 0) evenCount++;
                    else oddCount++;
                }
                String odd = "\n奇数:" + oddCount + "个\n";
                String even = "偶数:" + evenCount + "个";
                //得到奇数偶数个数之后写入
                fw.write(odd);
                fw.write(even);
                System.out.println(odd +even + " 追加写入完成");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

英文排序 &#8203;

在一个磁盘的文件里保存26个英文小写字母(乱序),将他们读入内存中,进行排序,
把排好顺序的数再重新追加写到磁盘的该文件中。

tips:

  • IO

  • 排序

  • 异常处理

    java
        public static void main(String[] args) {
            //生成乱序26个英文小写字母
    //        ArrayList<Character> cs = new ArrayList<>();
    //        for (int i = 'a'; i <= 'z'; i++)
    //            cs.add((char) i);
    //        Collections.shuffle(cs);
    //        StringBuilder sb = new StringBuilder();
    //        cs.forEach(character -> sb.append(character));
    //        System.out.println(sb.toString());
            try(BufferedReader fr = new BufferedReader(new FileReader("test.txt"));
                BufferedWriter fw = new BufferedWriter(new FileWriter("test.txt",true));
            ) {
                //读入内存
                String s = fr.readLine();
                char[] cs = s.toCharArray();
                //排序
                Arrays.sort(cs);
                System.out.println(Arrays.toString(cs));
                //追加写入
                fr.close();
                fw.newLine();
                fw.write(new String(cs));
                System.out.println("完成");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

递归查找并复制 &#8203;

递归查找指定目录中(包括子目录中),所有的.java文件,并且,
把所有这些找到的java文件,复制(是复制不是移动)到一个指定的目录下

tips:

  • 递归
  • File API
  • 文件复制
  • 异常处理
java
    public static void main(String[] args) {
        File file = new File("E:\\JAVA\\ideaProj");
        findRecursive(file);
    }

    private static void findRecursive(File srcfile) {
        for (File listFile : srcfile.listFiles()) {
            if(listFile.isDirectory())
                findRecursive(listFile);
            else{
                if(listFile.getName().endsWith(".java")){//找到一个复制一个
                    String newPath = "E:\\" + listFile.getName();
                    copyJava(listFile,new File(newPath));
                }
            }
        }
    }
    private static void copyJava(File srcFile,File desFile){
        try (BufferedReader br = new BufferedReader(new FileReader(srcFile));
             BufferedWriter bw = new BufferedWriter(new FileWriter(desFile));){
            char[] chars = new char[8192];
            int readCount = 0;
            while((readCount = br.read(chars))!=-1){
                bw.write(chars,0,readCount);
            }
            System.out.println(srcFile.getAbsolutePath()+" 文件复制 -----> "+desFile.getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }

文件内容加密 &#8203;

设计一个方法

public static void encodeFile(File encodingFile, File encodedFile);

在这个方法中把encodingFile的内容进行加密,然后保存到encodedFile文件中。

加密算法:
数字:
如果不是9的数字,在原来的基础上加1,比如5变成6, 3变成4
如果是9的数字,变成0
字母字符:
如果是非z字符,向右移动一个,比如d变成e, G变成H
如果是z,z->a, Z-A。
字符需要保留大小写
非字母字符
比如',&^ 保留不变,中文也保留不变

文件内容解密 &#8203;

解密上面加密的文件

java
    public static void main(String[] args) {
        File fileOrginal = new File("test.txt");
        File fileEncode = new File("test_encode.txt");
        File fileDecode = new File("test_decode.txt");
        encodeFile(fileOrginal,fileEncode);
        decodeFile(fileEncode,fileDecode);
    }
    public static void encodeFile(File encodingFile, File encodedFile){
        try(BufferedReader br = new BufferedReader(new FileReader(encodingFile));
            BufferedWriter bw = new BufferedWriter(new FileWriter(encodedFile))) {
            int c = 0;
            while((c = br.read())!=-1){
                if(Character.isDigit(c)){
                    c = digitAdd(c);
                    bw.write(c);
                }else if(isLetter(c)){
                    c = letterAdd(c);
                    bw.write(c);
                }else bw.write(c);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("加密完成");
    }
    public static void decodeFile(File encodedFile, File decodedFile){
        try(BufferedReader br = new BufferedReader(new FileReader(encodedFile));
            BufferedWriter bw = new BufferedWriter(new FileWriter(decodedFile))) {
            int c = 0;
            while((c = br.read())!=-1){
                if(Character.isDigit(c)){
                    c = digitSub(c);
                    bw.write(c);
                }else if(isLetter(c)){
                    c = letterSub(c);
                    bw.write(c);
                }else bw.write(c);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("解密完成");
    }
    public static int digitAdd(int c){
        if(c == '9') return '0';
        else return (c+1);
    }
    public static int digitSub(int c){
        if(c == '0') return '9';
        else return (c-1);
    }
    public static int letterAdd(int c){
        if(c == 'z') return 'a';
        if(c == 'Z') return 'A';
        return (c+1);
    }
    public static int letterSub(int c){
        if(c == 'a') return 'z';
        if(c == 'A') return 'Z';
        return (c-1);
    }
    public static boolean isLetter(int c){
        if(c >= 'a' && c <= 'z') return true;
        if(c >= 'A' && c <= 'Z') return true;
        return false;
    }
}

多线程 &#8203;

多线程实现方式一 &#8203;

使用多线程实现方式一 ,多个文件一起复制功能(3个线程复制3个文件),并在控制台显示复制的进度,进度以百分比表示
使用多线程实现方式一 ,多个文件一起复制功能,
并在控制台显示复制的进度,进度以百分比表示

例如:把文件A复制到E盘某文件夹下,在控制台上显示“XXX文件已复制xx%”,
“XXX文件已复制xxx%”……“XXX文件已复制xxx%”,“XXX复制完成!

tips:

  • Thread

  • 文件复制

    java
    public class Test2 {
        static long begin;
        static long end;
        public static void main(String[] args) {
            begin = System.currentTimeMillis();
            new Cpoy("线程1","E:\\wangdao\\pdf\\疯狂JAVA讲义第5版.pdf","E:\\1.pdf").start();
            new Cpoy("线程2","E:\\wangdao\\pdf\\Head First设计模式.pdf","E:\\2.pdf").start();
            new Cpoy("线程3","E:\\wangdao\\pdf\\阿里巴巴Java开发手册(详尽版).pdf","E:\\3.pdf").start();
        }
    }
    
    class Cpoy extends Thread{
        String source;
        String des;
        Cpoy(String name,String s,String d){
            super(name);
            this.source = s;
            this.des = d;
        }
        @Override
        public void run() {
            ioCopy(this.source, this.des);
        }
        private void ioCopy(String source,String des) {
            File sourceFile = new File(source);
            long len = sourceFile.length();
            double hasRead = 0;
            try(BufferedInputStream bi = new BufferedInputStream(new FileInputStream(sourceFile),16384);
                BufferedOutputStream bs = new BufferedOutputStream(new FileOutputStream(des),16384);) {
                byte[] bytes = new byte[16384];//16kb
                int readCount = 0;
                while((readCount = bi.read(bytes))!= -1){
                    hasRead += readCount;
                    double percent = hasRead / len * 100;
                    System.out.printf("%s---%s 文件已复制%.2f%%\n"
                                      ,Thread.currentThread().getName(),sourceFile.getName(),percent);
                    bs.write(bytes,0,readCount);
                }
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
    
            System.out.println("完成");
            Test2.end = System.currentTimeMillis();
            System.out.println(Test2.end - Test2.begin);
        }
    }

线程控制API &#8203;

Join &#8203;

现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?
利用线程控制API

tips:

  • join
java
public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread("t1");
        MyThread t2 = new MyThread("t2");
        MyThread t3 = new MyThread("t3");
        t1.start();
        t1.join();
        t2.start();
        t2.join();
        t3.start();
    }
}
class MyThread extends Thread{
    MyThread(String name){
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {

            System.out.println(Thread.currentThread().getName() + "在执行"+i);
        }
    }
}

综合 &#8203;

要求: 从0-9这10个数中

- 生成一个长度是3的随机数字,把这个字符串当作密码
- 创建一个破解线程,使用穷举法,匹配这个密码,直到匹配到正确的
- 创建一个日志线程,将用过的字符串写入文件,这个日志线程设计为守护线程

提示: 破解线程把穷举法生成的可能密码放在一个容器中,
日志线程把这个容器中的密码并打印出来。(因为没有学集合,先用数组)
注意:  守护线程的特征,jvm中只有守护线程 jvm退出

可能出现的情况:
- 穷举完了 还没写入 程序就已经终止了
- 穷举完了 写了一半 程序就终止了
注意: 当Java进程中的所有线程都是守护线程时,JVM退出。

而日志线程打印破解过程日志,必然需要等待破解线程执行完毕后才能够完整打印这个过程。

所以日志线程打印破解过程,需要在破解线程执行完毕后再启动执行。思考一下,如何保证这一点呢?

tips:

  • setDaemon

  • sleep

  • IO

  • Random

  • equals

    java
    public class Test2 {
        static String pwd = getPwd(3);
        static String log[] = new String[1000];
        public static void main(String[] args) {
            System.out.println("得到的随机密码:"+pwd);
            Thread tCrack = new Thread(Test2::runCrack);
            Thread tLog = new Thread(Test2::runLog);
            tLog.setDaemon(true);
            tCrack.start();
            try {
                tCrack.join();//保证log[]数组中数据完整,最后再一次性写入
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tLog.start();
            try {
                Thread.sleep(1000);//主线程睡眠,保证tLog写入完成
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static String getPwd(int count){
            StringBuilder sb = new StringBuilder();
            Random rd = new Random();
            for (int i = 0; i < count; i++) {
                sb.append(rd.nextInt(10));
            }
            return sb.toString();
        }
        public static void runCrack(){
            for (int i = 0; i < 1000; i++) {
                String p = String.format("%03d", i);
                log[i] = p;
                if (p.equals(pwd)) {
                    System.out.println("匹配成功:" + pwd);
                    return;
                } else
                    System.out.println(p);
            }
        }
        public static void runLog(){
            try (FileWriter fw = new FileWriter("test.txt", true);) {
                for (String l : log) {
                    if (l != null)
                        fw.write(l + "\n");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
      }
    }

多线程实现方式二 &#8203;

使用多线程实现方式二 ,多个文件一起复制功能,
并在控制台显示复制的进度,进度以百分比表示

例如:把文件A复制到E盘某文件夹下,在控制台上显示“XXX文件已复制xx%”,
“XXX文件已复制xxx%”……“XXX文件已复制xxx%”,“XXX复制完成!

tips:

  • Runnable
  • 文件复制
  • 异常处理
java
public class Test2 {
    static long begin;
    static long end;
    public static void main(String[] args) {
        String p1 = "E:\\wangdao\\pdf\\疯狂JAVA讲义第5版.pdf";
        String p2 = "E:\\wangdao\\pdf\\Head First设计模式.pdf";
        String p3 = "E:\\wangdao\\pdf\\阿里巴巴Java开发手册(详尽版).pdf";
        begin = System.currentTimeMillis();
        new Thread(() ->
                ioCopy(p1, "E:\\1.pdf"),"线程1"
        ).start();
        new Thread(() ->
                ioCopy(p2,"E:\\2.pdf"),"线程2"
        ).start();
        new Thread(() ->
                ioCopy(p3,"E:\\3.pdf"),"线程3"
        ).start();
    }
    public static void ioCopy(String source,String des) {
        int buffSize = 16384*2;//32k
        File sourceFile = new File(source);
        long len = sourceFile.length();
        double hasRead = 0;
        try(BufferedInputStream bi = new BufferedInputStream(new FileInputStream(sourceFile),buffSize);
            BufferedOutputStream bs = new BufferedOutputStream(new FileOutputStream(des),buffSize);) {
            byte[] bytes = new byte[buffSize];
            int readCount;
            while((readCount = bi.read(bytes))!= -1){
                hasRead += readCount;
                double percent = hasRead / len * 100;
                System.out.printf("%s---%s 文件已复制%.2f%%\n",
                        Thread.currentThread().getName(),sourceFile.getName(),percent);
                bs.write(bytes,0,readCount);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        System.out.println("完成");
        Test2.end = System.currentTimeMillis();
        System.out.println(Test2.end - Test2.begin);
    }
}

线程安全 &#8203;

模拟迅雷下载 &#8203;

用多线程代码来模拟,迅雷用3个线程下载100M资源的的过程。

 每个线程每次,一次下载1兆(
 M)资源,直到下载完毕,即剩余的待下载资源大小为0

 (用一个整数表示资源大小,每次个线程每次下载多少兆(M), 剩余待下载资源就减少多少兆(M),

  模拟我们售票的代码实现,考虑多线程的数据安全问题)

tips:

  • synhronized/Lock
java
public class Test3 {
    static File srcFile = new File("E:\\wangdao\\100.txt");
    static File desFile = new File("E:\\100.txt");
    static int totalSize = 1024 * 1024 * 100;//写一个100M的文件
    public static void main(String[] args) throws IOException, InterruptedException {
        getFile100();//得到100M都是空格的文件
        //三个线程同时复制同一个文件,模拟下载过程
        MyRunnable target = new MyRunnable();
        target.bi = new BufferedInputStream(new FileInputStream(Test3.srcFile));
        target.bo = new BufferedOutputStream(new FileOutputStream(Test3.desFile), target.downSize);
        Thread t1 = new Thread(target, "线程1");
        Thread t2 = new Thread(target, "线程2");
        Thread t3 = new Thread(target, "线程3");
        t1.start();t2.start();t3.start();
        t1.join();t2.join();t3.join();//下载线程先执行
        new Thread(() ->{//用来关闭输入输出流的线程
                try {
                    target.bi.close();
                    target.bo.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println("多次释放");
                }
            }
        ).start();
    }
    private static void getFile100() {
        try (BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(srcFile))) {
            byte[] bytes = new byte[totalSize];//文件里放100M个空格
            bo.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        System.out.println("得到100M的文件");
    }
}

class MyRunnable implements Runnable {
    long totalSize = Test3.srcFile.length();
    double hasRead = 0;
    int downSize = 1024*1024;
    //多个线程操作共享文件,输入输出流也要共享
    BufferedInputStream bi ;
    BufferedOutputStream bo;
        @Override
    public void run() {
        while (true) {
            if (hasRead >= totalSize) {
                System.out.println("复制完成");
                return;
            }
            byte[] bytes = new byte[downSize];
            int readCount;
            synchronized (this) {
                try {
                    if ((readCount = bi.read(bytes)) != -1) {
                        hasRead += readCount;
                        bo.write(bytes, 0, readCount);
                        System.out.printf("%s---剩余%.1fkb---进度:%.2f%%\n", Thread.currentThread().getName(),
                                totalSize - hasRead,hasRead / totalSize * 100);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
           }
        }
    }
}

线程通信 &#8203;

wait notify &#8203;

要求2个线程交替打印1A2B3C4D...
wait notify
附加: 怎么保证某个线程先执行 比如先打印A 再打印1
=====通过flag标记做判断

tips:

  • wait
  • notify
  • synchronized
java
public class Demo {
    static boolean flag = false;
    public static void main(String[] args) {
        Thread tLetter = new Thread(Demo::runLetter, "线程1");
        Thread tDigit = new Thread(Demo::runDigit, "线程2");
        tLetter.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (flag)
            tDigit.start();
    }

    private synchronized static void runDigit() {
        try {
            for (int i = 1; i <= 26; i++) {
                System.out.println(Thread.currentThread().getName() + "---" + i);
                if (i == 26) i = 1;
                Thread.sleep(500);
                Demo.class.notify();
                Demo.class.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private synchronized static void runLetter() {
        try {
            for (int i = 'A'; i <= 'Z'; i++) {
                System.out.println(Thread.currentThread().getName() + "---" + (char) i);
                flag = true;
                if (i == 'Z') i = 'A';
                Thread.sleep(500);
                Demo.class.notify();
                Demo.class.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

wiat notifyAll() &#8203;

启动3个线程打印递增的数字使用wait notifyAll (选做)

 线程1先打印1,2,3,4,5, 然后是线程2打印6,7,8,9,10,
 然后是线程3打印11,12,13,14,15. 接着再由线程1打印16,17,18,19,20....以此类推,
 直到打印到75
java
public class Demo3 {
    static int missionNum = 1;
    static int num = 1;
    static int stopNum = 75;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(Demo3::runT1, "线程1");
        Thread t2 = new Thread(Demo3::runT1, "线程2");
        Thread t3 = new Thread(Demo3::runT1, "线程3");
        t1.start();
        Thread.sleep(3000);
        if (missionNum == 2)
            t2.start();
        Thread.sleep(3000);
        if (missionNum == 3)
            t3.start();
    }

    public synchronized static void runT1() {
        try {
            while (num<stopNum) {
                for (int i = 0; i < 5; i++) {//打印5次再wait
                    System.out.println(Thread.currentThread().getName() + "---" + num++);
                    Thread.sleep(500);
                }
                //改变任务编号为下一个
                missionNum = missionNum == 3 ? 1 : missionNum + 1;
                Demo3.class.notifyAll();
                //得到当前任务是第几个
                int m = Integer.parseInt(Thread.currentThread().getName().substring(2));
                //不是对应的任务编号就一直等待, n>=75不用等待,否则程序无法结束
                while (missionNum != m && num<stopNum) {
                    Demo3.class.wait();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程池 &#8203;

创建一个任务,它将睡眠随机0-10s, 然后显示睡眠时间并退出。
创建并运行一定数量的这个任务
ps:利用线程池

tips:

  • Executors

  • sleep

    java
    public class Demo {
        public static void main(String[] args) {
            runSleep rs = new runSleep();
            ExecutorService pool = Executors.newFixedThreadPool(3);
            for (int i = 0; i < 10; i++) {
                pool.submit(rs);
            }
            pool.shutdown();
        }
    }
    class runSleep implements Runnable{
        Random rd = new Random();
        @Override
        public void run() {
            int se = rd.nextInt(11);
            try {
                Thread.sleep(se*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "---睡眠了"+se+"秒");
        }
    }

多线程实现方式三 &#8203;

用Callable的方式去实现之前的多线程文件复制
复制成功后,返回复制成功的文件路径,并利用Callable的方式接收返回值,
然后把这几个路径都保存到新的文件中.

tips:

  • Callable

  • IO

  • Thread

  • FutureTask

    java
    public class Demo {
        public static void main(String[] args) throws Exception{
            String p1 = "E:\\wangdao\\pdf\\Java多线程编程核心技术(第2版).pdf";
            String p2 = "E:\\wangdao\\pdf\\Java编程思想第四版完整中文高清版.pdf";
            String p3 = "E:\\wangdao\\pdf\\阿里巴巴Java开发手册(详尽版).pdf";
            FutureTask<String> ft1 = new FutureTask<>(new callCopy(p1, "E:\\"));
            FutureTask<String> ft2 = new FutureTask<>(new callCopy(p2, "E:\\"));
            FutureTask<String> ft3 = new FutureTask<>(new callCopy(p3, "E:\\"));
            new Thread(ft1,"线程1").start();
            new Thread(ft2,"线程2").start();
            new Thread(ft3,"线程3").start();
            String s1 = ft1.get();
            String s2 = ft2.get();
            String s3 = ft3.get();
            FileWriter fw = new FileWriter("E:\\copySuccess.txt");
            fw.write(s1+System.lineSeparator()+s2+System.lineSeparator()+s3);
            fw.close();
        }
    }
    
    class callCopy implements Callable<String> {
        String srcPath;
        String desPath;
        File srcFile ;
        public callCopy(String srcPath, String desPath) {
            this.srcPath = srcPath;
            this.desPath = desPath + this.srcPath.substring(this.srcPath.lastIndexOf(File.separator));
            srcFile = new File(srcPath);
        }
    
        @Override
        public String call() throws Exception {
            BufferedInputStream bi = new BufferedInputStream(new FileInputStream(srcFile));
            BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(desPath));
            int readCount;
            int buffSize = 8192;
            int hasRead = 0;
            byte[] bytes = new byte[buffSize];
            while ((readCount = bi.read(bytes)) != -1) {
                bo.write(bytes, 0, readCount);
                hasRead += readCount;
                System.out.println(Thread.currentThread().getName() + " 复制 " + srcPath +
                        "--->" + desPath + "完成" + 
                                   String.format("%.2f%%",hasRead*1.0/srcFile.length()*100));
            }
            System.out.println(srcPath+" 复制完成!");
            bi.close();
            bo.close();
            return desPath;
        }
    }

定时任务 &#8203;

写1个定时任务,3s的延迟后,每隔5s向文件中写入一行当前时间

tips:

  • TimerTask
  • Timer
  • SimpleDateFormat
java
public class Temp{
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new MyTimerTask(),3000,5000);
    }
}
class MyTimerTask extends TimerTask{

    @Override
    public void run() {
        try(FileOutputStream fo = new FileOutputStream("test.txt",true)){
            String t = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(System.currentTimeMillis());
            fo.write((t+System.lineSeparator()).getBytes());
            System.out.println(t+"写入成功一次");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

分发礼物 &#8203;

有100份礼物,小红小明两人同时发送,当身下礼品小于10份的时候,不在送出。
利用多线程模拟该过程,分别打印小红小明各自送出多少礼物
java
public class Demo {
    public static void main(String[] args) {
        runGift rg = new runGift();
        new Thread(rg,"小红线程").start();
        new Thread(rg,"小明线程").start();
    }
}
class runGift implements Runnable{
    int gifts = 100;
    int hong = 0;
    int ming = 0;
    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (gifts < 10) break;
                if (Thread.currentThread().getName().equals("小明线程")) ming++;
                else hong++;
                System.out.println(Thread.currentThread().getName() + 
                                   " 送出了第" + (100 - gifts + 1) + "份礼物");
                gifts--;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("小明送出了:"+ming+"\n小红送出了:"+hong);
    }
}

三种方式抢票 &#8203;

分别用方式一,方式二和方式三实现多线程抢票,注意共享数据的线程安全问题。方式三返回每个线程抢到多少张票

java
public class Test4 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //方式一抢票 继承Thread类
        //getT1();
        //方式二 实现Runnable接口
        //getT2();
        //方式三callable接口,返回三个线程各自抢到多少张票
        getT3();
    }

    private static void getT3() throws InterruptedException, ExecutionException {
        FutureTask<String> ft1 = new FutureTask<>(new callTicket());
        FutureTask<String> ft2 = new FutureTask<>(new callTicket());
        FutureTask<String> ft3 = new FutureTask<>(new callTicket());
        new Thread(ft1,"线程1").start();
        new Thread(ft2,"线程2").start();
        new Thread(ft3,"线程3").start();
        System.out.println(ft1.get());
        System.out.println(ft2.get());
        System.out.println(ft3.get());
    }

    private static void getT2() {
        Runnable target = new RGetTicket();//线程的任务
        new Thread(target,"线程1").start();
        new Thread(target,"线程2").start();
        new Thread(target,"线程3").start();
    }

    private static void getT1() {
        new TGetTicket("线程1").start();
        new TGetTicket("线程2").start();
        new TGetTicket("线程3").start();
    }
}
class RGetTicket implements Runnable{
    int ticket = 100;
    @Override
    public void run() {
        while(true){
            synchronized (this) {
                try {
                    Thread.sleep(100);//模拟网络延时,共享数据不安全
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(ticket <= 0) return;
                System.out.println(Thread.currentThread().getName()+
                        "-----抢到了第"+(ticket--)+"张票");
            }
        }
    }
}
class TGetTicket extends Thread{
    TGetTicket(String name) {
        super(name);
    }
    static int ticket = 100;
    @Override
    public void run() {
        while(true){
            synchronized (TGetTicket.class) {
                try {
                    Thread.sleep(100);//模拟网络延时,共享数据不安全
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(ticket <= 0) return;
                System.out.println(getName()+"-----抢到第"+(ticket--)+"张票");
            }
        }
    }
}
//返回三个线程各自抢到多少张票
class callTicket implements Callable<String>{
    int tCount = 0;
    static int ticket = 100;
    @Override
    public String call() throws Exception {
        while(true){
            synchronized (callTicket.class) {
                if(ticket<=0) break;
                Thread.sleep(10);
                String tName = Thread.currentThread().getName();
                System.out.println(tName+ " 抢到了第"+(ticket--)+"张票");
                tCount++;
            }
        }
        return Thread.currentThread().getName()+"总共抢到:"+tCount;
    }
}

网络编程 &#8203;

UDP &#8203;

参考上课代码, 实现跟同桌聊天

tips:

  • DatagramPacket
  • DatagramSocket
  • 多线程
java
public class Server {
    public static void main(String[] args) throws IOException {
        //创建ServerSocket,监听端口1315
        ServerSocket serverSocket = new ServerSocket(1315);
        System.out.println("服务器启动,监听端口1315");
        while (true) {
            //阻塞等待连接
            Socket accept = serverSocket.accept();
            System.out.println(accept.getRemoteSocketAddress().toString() + "接入服务器");
            //连接到一个客户端后,得到对应的客户端socket
            new Thread(new ReceiveTask(accept)).start();
        }
    }
}

class ReceiveTask implements Runnable {
    Socket clientSocket;
    InputStream is;
    public ReceiveTask(Socket clientSocket) {
        this.clientSocket = clientSocket;
    }

    @Override
    public void run() {
        //得到输入流,读取消息
        try {
            is = clientSocket.getInputStream();
            while (true) {
                int readCount;
                byte[] bytes = new byte[1024];
                System.out.println();
                while ((readCount = is.read(bytes)) != -1) {//客户端断开连接会在这里报错
                    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
                    String dataTime = sf.format(System.currentTimeMillis());
                    System.out.printf("[%s] %s\n", dataTime, 
                                      clientSocket.getRemoteSocketAddress().toString());
                    System.out.println(new String(bytes, 0, readCount));
                }
                System.out.println("while end");
                break;
            }
        } catch (IOException e) {
            try {
                System.out.println(clientSocket.getRemoteSocketAddress().toString() + "断开连接");
                is.close();
                clientSocket.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        try {
            System.out.println(clientSocket.getRemoteSocketAddress().toString() + "断开连接");
            is.close();
            clientSocket.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}

public class Client {
    public static void main(String[] args) throws IOException {
        //创建socket对象,指定要连接的服务端地址,建立连接
        // 客户端的端口号动态分配
        Socket socket = new Socket("127.0.0.1", 1315);
        System.out.println("成功连接服务器");
        //成功建立连接后,获得该socket的输出流
        OutputStream outputStream = socket.getOutputStream();
        Scanner sc  = new Scanner(System.in);
        while(true){
            String msg = sc.nextLine();
            if("exit".equals(msg)) break;
            //往输出流里写东西
            byte[] bytes = msg.getBytes();
            outputStream.write(bytes);
            System.out.println("----发送成功----");
        }
        //关闭输出流 关闭socket
        socket.shutdownOutput();
        socket.close();
    }
}

TCP &#8203;

参考上课代码实现

  1. 客户端键盘录入,服务器输出文本文件
  2. 客户端文本文件,服务器输出到控制台
  3. 客户端文本文件,服务器输出文本文件

tips:

  • Socket
  • ServerScoket
  • IO
java
public class Server {
    public static void main(String[] args) throws IOException {
        //创建ServerSocket,监听端口1315
        ServerSocket serverSocket = new ServerSocket(1315);
        //阻塞等待客户端连接
        Socket accept = serverSocket.accept();
        //得到客户端输入流--->本地文件输出流
        InputStream cIn = accept.getInputStream();
        FileOutputStream fo = new FileOutputStream("test.txt");
        int readCount;
        byte[] bytes = new byte[1024];
        while((readCount=cIn.read(bytes))!=-1){
            fo.write(bytes,0,readCount);
            fo.write(System.lineSeparator().getBytes());
            System.out.println(new String(bytes,0,readCount));
        }
        //关闭
        fo.close();
        cIn.close();
        accept.close();
        serverSocket.close();
    }
}
public class Client {
    public static void main(String[] args) throws IOException {
        //创建Socket对象,传入ip端口号,建立连接
        Socket socket = new Socket("127.0.0.1", 1315);
        //得到socket对象的输出流
        OutputStream outputStream = socket.getOutputStream();
        //从控制台得到数据源,写入输出流
        //sendByConsole(outputStream);
        //从文件中读取数据源
        FileInputStream fi = getByFile(outputStream);
        //关闭
        fi.close();
        socket.shutdownOutput();
        socket.close();
    }

    private static FileInputStream getByFile(OutputStream outputStream) throws IOException {
        FileInputStream fi = new FileInputStream("time.txt");
        byte[] bytes = new byte[1024];
        int readCount;
        while((readCount = fi.read(bytes))!=-1){
            outputStream.write(bytes,0,readCount);
        }
        return fi;
    }

    private static void sendByConsole(OutputStream outputStream) throws IOException {
        Scanner sc = new Scanner(System.in);
        while(true){
            String msg = sc.nextLine();
            if("exit".equals(msg)) break;
            outputStream.write(msg.getBytes());
        }
    }
}

反射 &#8203;

修改任意对象指定成员变量的值 &#8203;

补全代码

java
public class Work1 {
 /**
  *
  * @param targetObj  要修改成员变量值的目标对象
  * @param fieldName  对象中要修改的成员变量的名字
  * @param newValue   要修改成员变量值的新值
  */

public static void setAll(Object targetObj, String fieldName, Object newValue) {

        }
}

tips:

  • 反射API
  • Field
java
    public static void setAll(Object targetObj, String fieldName, Object newValue)
        throws IllegalAccessException {
        Class c = targetObj.getClass();
        for (Field declaredField : c.getDeclaredFields()) {
            if (declaredField.getName().equals(fieldName)) {
                declaredField.set(targetObj,newValue);
                System.out.println("修改成功");
                return;
            }
        }
    }

创建学生对象 &#8203;

使用反射机制完成学生对象的创建并输出学生信息。

要求:

(1)定义一个学生类Student,其中包含姓名(String)、年龄(int)、成绩(int)的属性。

(2)编写带参与无参构造方法。

(3)重写父类的toString()方法用于输出学生的信息。

(4)编写测试类TestStudent,从键盘录入学生的信息格式为(姓名:年龄:成绩)一次性录入使用“:”分隔,举例(张三:20:90)。

(5)使用String类的split方法按照“:”进行分隔。

(6)调用Constructor的newInstance()方法并用分隔后的信息初始化学生对象。

(7)调用重写父类的toString()方法将学生信息进入输出显示。
java
public class Demo2 {
    public static void main(String[] args) throws Exception {
        TestStudent.inputStu();
    }
}
class Student{
    String name;
    int age;
    int score;

    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public Student() {
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
class TestStudent{
    public static void inputStu() throws Exception {
        System.out.println("输入学生信息:格式为:姓名:年龄:成绩");
        Scanner sc  = new Scanner(System.in);
        String s = sc.nextLine();
        Class c = Student.class;
        String[] ss = s.split(":");
        Constructor constructor = c.getConstructor(String.class, int.class, int.class);
        Object o = constructor.newInstance(ss[0], Integer.parseInt(ss[1]), Integer.parseInt(ss[2]));
        System.out.println(o);
    }
}

配置文件 &#8203;

完成如下方法,要求该方法能调用,指定配置文件中,指定类中指定的普通成员方法(无参方法) 假设指定类中一定有默认构造方法

properties配置文件中内容如下: className=com.cskaoyan.day22.Student methodName=study

java
public class Work2 {

    /**
    *
    * @param configFilePath  表示配置文件的路径
    */
/*
public void callTargetMethod(String configFilePath) {


        }
   }

tips:

  • Properties
  • 反射API
  • Method
java
    public static void callTargetMethod(String configFilePath) throws Exception {
        Properties p = new Properties();
        p.load(new FileInputStream(configFilePath));
        String className = p.getProperty("className");
        String methodName = p.getProperty("methodName");
        System.out.println("className = " + className);
        System.out.println("methodName = " + methodName);
        Class c = Class.forName(className);
        //获取构造函数,构造一个对象
        Constructor constructor = c.getConstructor();
        constructor.setAccessible(true);
        Object o = constructor.newInstance();
        //获取指定方法,invoke
        Method method = c.getMethod(methodName);
        method.setAccessible(true);
        method.invoke(o);
    }

综合 &#8203;

配置文件与网络编程(UDP)结合练习

自动回复机器人

定义2个类 Sender,Receiver
    Sender向Receiver发送数据,并且接收Receiver的数据
    Receiver接收Sender的数据,并且根据Sender发的数据进行自动回复
如何自动回复?
    定义一个数据字典,说白了就是存放一些Receiver可能的回答,将这些可能的回答放在配置文件中
    比如:
    Sender发送 "在吗"? Receiver回复 "在", 那么这个"在"就事先放到配置文件里面---->  在吗=在
    Receiver在收到Sender发送的消息的时候,去数据字典里(也就是配置文件)去找相应的回答,如果没找到就回复 "我没听懂你在说什么"
    回复的内容可以自由发挥,都是以key=value的形式放在配置文件里就行

tips:

  • UDP
  • Properties
  • String

注解d &#8203;