• Java基础查漏补缺(2)


    Java基础查漏补缺(2)

    • apache和spring都提供了BeanUtils的深度拷贝工具包

    • +=具有隐形的强制转换

    • object类的equals()方法容易抛出空指针异常

      String a=null;
      /*使用a.equals(b)抛出异常
      优化方法1,若b为常量,即a.equals("test")
      此时调用"test".equals(a)可避免空指针错误
      优化方法2,使用Objects工具类:java.util.Objects
      调用Objects.equals(a,b)可避免空指针错误
      */
      
    • cal.get(Calendar.MONTH)从零开始返回,也就是说January(一月)返回0,因此int month = cal.get(Calendar.MONTH)+1才是正确月份;同时,cal.get(Calendar.DAY_OF_WEEK)是从周日开始,返回1,周一返回2,因此该值-1才是正确星期,同时还需要判定是否为周日(真难用处理星期可以使用一个从周日开始的String[ ]数组,直接使用String[ i-1 ]来进行输出舒服点了

    • jvm有String常量池(注意是 常 量 池,常量。盯久了不认识这俩字了),但是new String对象是在堆中的,new String拼接也是在堆中,不在常量池,字符串还有个inertn()方法,用于获取常量池引用:s2=s1.intern(),此时s1==s2为true

    • DateFormat为抽象类,格式化处理日期使用SimpleDateFormat类

    • Calendar类内部维护了一个fields[]数组,存储时间信息,get()方法传入的常量值就是数组下标。如cal.get(Calendar.YEAR)cal.get(1)是一样的(CalendarYEAR 值为 1)

    • 基本类型快速转化字符串可以加空字符串。如Sting s=1+"";

    • method ( int... arr )可变参数,不用构件数组而是直接传递多个参数。如void method(int... arr);method(1,2,3,4);

    JVM内存分布

    栈(也叫虚拟机栈)、堆,方法区,寄存器(也叫程序计数器),本地方法栈

    栈:Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法。因此递归很容易爆栈233333.

    堆:存放数组与对象,对象不被引用之后会被垃圾回收。

    方法区:保存.class文件,常量池,(还有静态方法?)

    具体可以看这个博客,写的很清楚。

    Comparable和Comparator

    Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。

    Comparator强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(如Collections.sort或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。

    总结:重写compareTo(Object ob)方法时,ob为第一个元素,this是后一个元素。因此,this-ob即为升序(与默认相同),如果是引用类型,直接调用compareTo方法(默认升序,想降序在前面加 - 返回反数即可)。compare()方法同理,第一个参数为后面的元素,第二个参数为前面的元素,因此,o1-o2为升序,o2-o1为降序(这两个函数返回值的意义为:整数:表示大于,0表示等于,负数表示小于)

    Collections.sort(list, new Comparator<Student>() {
        @Override
        public int compare(Student o1, Student o2) {
             int result = o2.getAge()-o1.getAge();//以学生的年龄降序
            //可以编写多个规则:
            /*
            	if(result==0){//第一个规则相同时 下一个规则 姓名的首字母 升序
                  result = o1.getName().charAt(0)-o2.getName().charAt(0);
              }
            */
            return result;
        }
    });
    /*这里,注意,o1是后面的元素,o2是前面的元素
      返回 1、0 表示不交换,-1表示交换,同理正数和0不交换,负数交换。
     */
    //lambda
    Collections.sort(list, (o1, o2) -> {
                return o2.getAge()-o1.getAge();
    });
    

    List、Set和数组互转

    1. list、set转数组:

      List<String> list = new ArrayList<String>();
      /*list.toArray()无参方法返回Object数组,用Object[]接收强转会抛异常;
        list.toArray(数组):
        	若数组长度小于list长度,转化失败,数组内容不变;
        	若长度相等,则正好转化;
        	若数组长度大于list长度,可以转化,但是最后一个元素的下一个会被赋null,也就是
        	数组[list.size()]这个位置会被赋null;
       */
      String[] array=list.toArray(new String[list.size()]);
      
    2. 数组转list、set:

      List<String> list = new ArrayList<>(Arrays.asList(array));
      Set<String> set = new HashSet<>(Arrays.asList(array))
      //或者使用Collections.adAll()方法:
      List<String> list = new ArrayList<String>(array.length);//list
      Set<String> set = new HashSet<String>(array.lenth);//set
      Collections.addAll(list, array);
      Collections.addAll(set,array);
      
    3. list、set互转:

      Set<String> set = new HashSet<>(list);
      List<String> list = new ArrayList<>(set);
      //毕竟都是Collection┓( ´∀` )┏
      

    用流装箱拆箱还不是太明白,以后再看吧。

    上面三种方式不能用于基本类型,因此需要对基本类型数组装箱拆箱,可以使用JDK8的Stream方式Integer[] ints= IntStream.of(arr).boxed().collect(Collectors.toList()).toArray(new Integer[0]);List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList());(这里是错的,不是所有的基本类型,试了下char和boolean数组不能用,int和double可以,具体什么原因以后再更)

    用Stream处理int数组

    更新:使用Stream进行拆装箱

    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
     
    public class Main {
        public static void main(String[] args) {
            int[] data = {4, 5, 3, 6, 2, 5, 1};
     
            // int[] 转 List<Integer>
            List<Integer> list1 = Arrays.stream(data).boxed().collect(Collectors.toList());
            // Arrays.stream(arr) 可以替换成IntStream.of(arr)。
            // 1.使用Arrays.stream将int[]转换成IntStream。
            // 2.使用IntStream中的boxed()装箱。将IntStream转换成Stream<Integer>。
            // 3.使用Stream的collect(),将Stream<T>转换成List<T>,因此正是List<Integer>。
     
            // int[] 转 Integer[]
            Integer[] integers1 = Arrays.stream(data).boxed().toArray(Integer[]::new);
            // 前两步同上,此时是Stream<Integer>。
            // 然后使用Stream的toArray,传入IntFunction<A[]> generator。
            // 这样就可以返回Integer数组。
            // 不然默认是Object[]。
     
            // List<Integer> 转 Integer[]
            Integer[] integers2 = list1.toArray(new Integer[0]);
            //  调用toArray。传入参数T[] a。这种用法是目前推荐的。
            // List<String>转String[]也同理。
     
            // List<Integer> 转 int[]
            int[] arr1 = list1.stream().mapToInt(Integer::valueOf).toArray();
            // 想要转换成int[]类型,就得先转成IntStream。
            // 这里就通过mapToInt()把Stream<Integer>调用Integer::valueOf来转成IntStream
            // 而IntStream中默认toArray()转成int[]。
     
            // Integer[] 转 int[]
            int[] arr2 = Arrays.stream(integers1).mapToInt(Integer::valueOf).toArray();
            // 思路同上。先将Integer[]转成Stream<Integer>,再转成IntStream。
     
            // Integer[] 转 List<Integer>
            List<Integer> list2 = Arrays.asList(integers1);
            // 最简单的方式。String[]转List<String>也同理。
     
            // 同理
            String[] strings1 = {"a", "b", "c"};
            // String[] 转 List<String>
            List<String> list3 = Arrays.asList(strings1);
            // List<String> 转 String[]
            String[] strings2 = list3.toArray(new String[0]);
     
        }
    }
    

    参见此博文

    数组打乱

    一种方式是转化list,使用Collections.shuffle(list)进行打乱,另一种方式是:

    从数组的最后一个位置(假设下标是n)开始向前扫描,然后随机生成一个0到n之间的随机数(这里必须包括n,不然最后一个数一定会变),假设该随机数是r1,然后将数组最后一个位置(下标n)与r1位置互换,之后开始扫面下一个数(下标为n-1),然后随机生成一个0到(n-1)之间的随机数,假设该随机数是r2,然后将数组倒数第二个位置(下标为n-1)与r2位置互换,然后继续扫面下一个数(下标为n-2),就这样一直迭代下去。在这个迭代过程中,可以保证扫面点左边的数字都是尚未确定位置的,而右边的数字都是已经安排好位置的。

    //若为基本类型则修改掉泛型即可 
    public static <T>  void shuffle(T[] arr) {
            for (int i = arr.length; i > 0; i--) {
                int r = new Random().nextInt(i);//取值0到lenth-1
                T temp = arr[i - 1];
                arr[i - 1] = arr[r];
                arr[r] = temp;  
            }
    }
    

    一个简单的双色球问题

    遇见个挺简单的题,但是由这个题扩展出许多有意思的东西包括上面的装箱和数组打乱

    做题记录
    import java.util.*;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    
    /*
    十二、双色球规则:双色球每注投注号码由6个红色球号码和1个蓝色球号码组成。红色球号码从1—33中选择;
        蓝色球号码从1—16中选择;请随机生成一注双色球号码。(要求同色号码不重复)//这里理解错误
     */
    public class Test12 {
        public static void main(String[] args) {
    //        Integer[] nums = makeBalls();这是第一种方法
            int[] nums = makeballs2();
            for (int i = 0; i < nums.length; i++) {
                System.out.print(nums[i] + " ");
            }
        }
    
        //思路1:用数组双循环判定是否重复
        //思路2:这里使用了set的不能重复的性质
        //用Integer[]代替int[],因为set转数组时不能为基本类型
        public static Integer[] makeBalls() {
            Set<Integer> balls = new HashSet<>();
            while (balls.size() < 6) {
                balls.add(new Random().nextInt(33) + 1);
            }
            while (balls.size() < 7) {
                balls.add(new Random().nextInt(16) + 1);
            }
            return balls.toArray(new Integer[balls.size()]);
        }
        
        //思路3:使用Arrays.binarySearch(数组,Key)判定是否重复
        //但是使用数组放入球的顺序很重要
        public static int[] makeballs2() {
            int[] balls = new int[7];
            for (int i = 0; i < balls.length; i++) {
                if (i == 0) {
                    balls[i] = new Random().nextInt(16) + 1;
                    continue;
                }
                int num = new Random().nextInt(33) + 1;
                //Arrays.sort(balls);排序会打乱顺序,就不能用i操作复制了,正常使用需要配合排序
                int some =Arrays.binarySearch(balls, num);
                if (some > 0) {
                    i--;
                } else {
                    balls[i] = num;
                }
            }
            //球放进去之后打乱,list可以使用Collections.shuffle(list)进行打乱
            //至于数组打乱见shuffle方法
    //        shuffle(IntStream.of(balls).boxed().collect(Collectors.toList()).toArray(new Integer[0]));
            shuffle2(balls);
            return balls;
        }
    
        public static <T>  void shuffle(T[] arr) {
            for (int i = arr.length; i > 0; i--) {
                int r = new Random().nextInt(i);//取值0到lenth-1
                T temp = arr[i - 1];
                arr[i - 1] = arr[r];
                arr[r] = temp;
            }
        }
        
        public static void shuffle2(int[] arr) {//这样就不用装箱了
            for (int i = arr.length; i > 0; i--) {
                int r = new Random().nextInt(i);//取值0到lenth-1
                int temp = arr[i - 1];
                arr[i - 1] = arr[r];
                arr[r] = temp;
            }
        }
    }
    

    同时知道了Arrays.binarySearch()的用法,这里贴两个链接,写的挺清楚的链接1链接2

    • IDEA在debug模式下用Alt+F8可以打开Evaluate Expression计算表达式窗口,可以动态获取某一个值(这是个神器!)

    Set

    Set判断元素是否重复通过调用被添加元素的hashCode()方法,因此,对于自定义类,需要重写hashCode()和equals()方法

    调用被添加元素的hashCode(),和HashSet中已有元素的hashCode比较是否相同
    如果不相同,直接存储
    如果相同,调用equals方法比较是否相同
    不相同,直接存储元素
    相同,认为是同一元素.不存储

    //这里是个Human类,有name,age成员变量       
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Human so = (Human) o;
        return (name.equals(so.name) && age == so.age);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    

    int[] arr;
    arr = {1, 2};//这种初始化形式只有在声明的时候才能用(之前没注意到这点)
    //这种是对的
    int[] arr = {1,2};
    //这种也是可以的
    int[] arr2;
    arr2 = new int[]{1,2,3};
    

    实现Runnable接口必须实现无参无返回值的run()方法


    主方法中String[] args的意思是Java程序在运行的时候可以传参,比如

    public class Test {
        public static void main(String[] args) {
            for (int i = 0; i < args.length; i++) {
                System.out.println(args[i]);
            }
        }
    }
    

    在控制台执行 javac Test.java编译
    java Test AAA BBB CCC运行
    运行结果会是:
    AAA
    BBB
    CCC
    即三个参数会在main方法运行前传入args数组


    • this在Java中属于一个隐式变量,记录着调用当前方法的对象的地址

    • 使用迭代器迭代list时,修改list会抛出ConcurrentModificationException异常

    //数组遍历有删除操作时需要i--进行回溯
    
    //这种数组型的遍历并不能完全移除
    for (int i = 0; i < list.size(); i++) {
        if (list.get(i).equals("b")){
            list.remove(i);
            //i--;//加入i--回溯可以移除
        }
    }
    // 倒序遍历是可以的,因为判定值与长度无关,但是不推荐
    for (int i = list.size() - 1; i >= 0; i--) {
        if (list.get(i).equals("b")){
            list.remove(i);
        }
    }
    //更优雅的是使用流~
    System.out.println(list.stream()
            .filter(s -> !s.equals("b"))
            .collect(Collectors.toList()));//赏心悦目~
    
    list.remove("b");//移除第一个找到的"b"
    
    //使用迭代器迭代list时,修改list会抛出ConcurrentModificationException异常
    for (String s : list) {
        if (s.equals("b")) {
            list.add("d");
            list.remove(s);
        }
    }
    
    
    String[] arr = {"D","B","A","B","C","A","A","B","D","C","D","D"};
    Stream<String> stream = Arrays.stream(arr).limit(5);
    List<String> list = stream.collect(Collectors.toList());
    
    //这里会抛出异常,因为流是不能被重用的!!!
    String[] strings = stream.toArray(String[]::new);
    
    • 集合不能存储基本类型,只能存储引用类型

    • Scanner的nextLine和nextInt一般不要混用,因为:nextLine遇到回车结束;nextInt遇到回车结束后会留下一个空格。因此混用时需要在nextInt下面再放一个nextLine来读取留下的那个空格

    • return可以结束方法,或者System.exit(0)结束虚拟机

    • 可以使用 在控制台输出规整的字符

    • Pattern和matcher的简单实用(先贴个博客再说):

      • Pattern对象由静态方法:Pattern.compile(regex)创建,
      • Pattern对象有个matcher(string)方法,参数为需要匹配的字符串,该方法返回Matcher对象
      • Matcher对象有三个主要方法,matches(),lookingAt(),find()
        • matches()返回布尔值,匹配整个字符串,也就是说整个字符串都需要符合正则规则,才会返回true
        • lookingAt()返回布尔值,从开头匹配,也就是说开头有符合正则的,就会返回true
        • find()返回布尔值,只要发现符合正则规则的,就会返回true
      • 也就是说,一个快捷的匹配方式为Pattern.compile(regex).matcher(string).find()就可以知道string里是否含有regex的字符串了。
      • Matcher有个成员方法group()可以返回匹配到的字符串,以及start()end()方法获得匹配到的字符串的开始和结束的下标。(想要matcher.group()方法,必须先matcher.find()方法,切忌!要不然总是报错)
      • 只有当匹配操作成功,才可以使用start(),end(),group()三个方法,否则会抛出java.lang.IllegalStateException,也就是当matches(),lookingAt(),find()其中任意一个方法返回true时,才可以使用。
    • properties.load(new FileInputStream(path))load方法执行后其参数流是打开状态。

  • 相关阅读:
    iOS 沙盒机制 持久存储 读写文件 NSFileManager
    关键字@synchronized
    整理UIImagePickerController、保存图片到相册问题
    关于MJRefresh的下拉加载数据bug
    tableView显示第一个cell有偏移问题
    iOS比较常用的第三方框架
    iOS 10 下的用户隐私访问相册等权限问题!
    diff: /../Podfile.lock: No such file or directory diff: /Manifest.lock: No such file or directory error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods in
    iOS的坑:ERRORITMS-90096: "Your binary is not optimized for iPhone 5
    CocoaPods 1.0之前版本无法pod install和pod update! 更新后CocoaPods 1.1.1 Podfile新的写法.
  • 原文地址:https://www.cnblogs.com/lixin-link/p/10991694.html
Copyright © 2020-2023  润新知