美文网首页
Java常用技巧

Java常用技巧

作者: GIT提交不上 | 来源:发表于2019-09-25 00:25 被阅读0次

1. 泛型数组合并

import java.util.ArrayList;
import java.util.Arrays;

public class Test {

    public static void main(String[] args){
        String[] strArray = {"hello", "world"};
        String[] newStrArray = {"like", "love"};

        ArrayList<String> list = new ArrayList<>(Arrays.asList(strArray));
        list.addAll(Arrays.asList(newStrArray));
        String[] resArray = Arrays.copyOf(list.toArray(),list.toArray().length,String[].class);
        for(String str:resArray) {
            System.out.println(str);
        }
    }
}

2. 值传递 & 引用传递

2.1 Java数据类型

  Java数据类型分为基本数据类型和引用数据类型,如图2-1所示:


图2-1 Java数据类型.png

2.2 值传递

  值传递:当基本类型作为参数传入方法时,无论该参数在方法内怎样被改变,外部的变量原型总是不变的。即方法操作的是参数变量(也就是原型变量的一个值的拷贝)改变的也只是原型变量的一个拷贝而已,而非变量本身。所以变量原型并不会随之改变。

public class Test {
    public void oprNum(int number){
        number++;
    }
    public static void main(String[] args){
        int a = 10;
        new Test().oprNum(a);
        System.out.println(a);   //10
    }
}

2.3 引用传递

  引用传递:当方法传入的参数为非基本类型时(也就是说是一个对象类型的变量), 方法改变参数变量的同时变量原型也会随之改变。方法操作参数变量时是拷贝了变量的引用(指向了对象所在的真实地址,而非对象本身),而后通过引用找到变量的真正地址,并对其进行操作。

public class Test {
    public void oprNum(StringBuilder builder){
        builder.append("world");
    }
    public static void main(String[] args){
        StringBuilder builder = new StringBuilder("hello");
        new Test().oprNum(builder);
        System.out.println(builder); //helloworld
    }
}

2.4 String参数传递

  String的存储实际上通过char[]来实现的,char[]定义为了常量,具有不可变性。当String作为参数传递时,确实是以地址传递过去的,但是在对String类型的变量的值进行改变时,由于String类的值是常量,在创建后不能更改,所以对String参数进行操作,相当于new String(),已经改变了String对象的地址了,所以实参不会改变。

图2-2 String源码

2.5 String不可变性

  所谓的string类型不可变,可以从对象的内存分配上来分析:当创建一个string类型的对象的的时候,如String a=“abc”,首先在“栈”内存中开辟空间保存String类型对象的引用a,且在“常量池”中开辟空间保存内容“abc”。并返回其内存地址给对象的引用a。(a只是字符串内存地址的引用
  当我们在操作a变量的时候,如:1)重新赋值(a=“bbb”),系统会首先查询"常量池",里面是否有”bbb“这个字符串。如果有会自动返回“bbb”这个字符串的地址给对象的引用a。如果没有就会创建字符串“bbb”,并返回地址给对象的引用a。而不是直接操作原对象a指向的保存“abc”内容的这个内存地址空间。从内存分析string是不可变的,指的是对象指向的字符串内容一旦在常量池中被创建和保存,就不会更改。当我们再重新赋值的时候,是给在栈内存中的对象的引用a,赋一个新的地址,而没修改原来值所在空间的地址
  2)使用方法修改其内容(a.replace("a","b");)。对内存的操作是同上的,系统依然会首先在”常量池“中,查找替换后字符串。如果没有找到相应的字符串。就会在常量池中保存一个新的字符串,并返回地址给变量的引用,同样是没有修改“常量池”中原来的值。
  当final修饰的变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化

  String.intern()方法在JDK1.7以后,如果字符串在字符串常量池中没有出现过,就会在字符串常量池中保存一个引用,指向堆中该字符串的实例。如果字符串在常量池中已经存在了,就返回常量池中的引用。

//True False
public static void main(String[] args) {
    String str1=new StringBuilder("java").append("虚拟机").toString();//执行完这行代码后,常量池中会有"java"和"虚拟机",但是不会有"java虚拟机"。
    System.out.println(str1.intern()==str1);
    String str2=new String("我喜欢java");  //执行完这行代码后,常量池中会有"我喜欢java"
    System.out.println(str2==str2.intern());
}

  可以通过反射打破这种不可变性。

public class Test {
    public static void main(String[] args){
        String str = "hello";
        try {
            Field field = String.class.getDeclaredField("value");
            field.setAccessible(true);
            char[] value = (char[])field.get(str);
            value[2] = 'k';
        }
        catch (NoSuchFieldException e){

        }
        catch(IllegalAccessException e){

        }
        System.out.println(str); //heklo
    }
}

3. List排序

  //自然排序
  ArrayList<Integer> list = new ArrayList<>();
  list.add(2);
  list.add(3);
  list.add(1);
  Collections.sort(list); //1,2,3

4. Java集合遍历

  Java集合框架图如下所示:


图4-1 Java集合框架图.gif

4.1 普通for循环遍历

  第一次删除2时,这个位置之后的所有元素都向前挪动一个位置,故索引值为2时对应的数值为4,可以手动设置i--恢复正常遍历顺序。使用list.set()方法可以修改列表元素,使用list.add()方法新增元素时会在list最后插入数据。

public class Test {
    public static void main(String[] args){
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(2);
        list.add(4);
        list.add(5);
        for(int i=0;i<list.size();i++){
            if(list.get(i) == 2) {
                list.remove(i);
                // i--;
            }
        }
      System.out.println(list);  //[1, 2, 4, 5]
    }
}

4.2 增强for循环遍历

  在增强for循环里删除或者添加集合元素,会报java.util.ConcurrentModificationException异常信息。

图4-2 报错信息.png
public class Test {
    public static void main(String[] args){
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(2);
        list.add(4);
        list.add(5);
        for (Integer a: list) {
            if (a == 2){
                list.remove(a);
            }

        }
      System.out.println(list); 
    }
}

  增强for循环本质上是隐式的iterator,由于在删除和添加的时候会导致modCount发生变化,但是没有重新设置expectedModCount,当使用list.remove()后遍历执行iterator.next()时,方法检验modCount的值和的expectedModCount值,如果不相等,就会报ConcurrentModificationException。

图4-3 checkForComodification函数源码.png {

4.3 迭代器遍历

  使用list方法的add()方法和remove()方法,同样会报java.util.ConcurrentModificationException异常错误,原因同4.2。

public class Test {
    public static void main(String[] args){
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(2);
        list.add(4);
        list.add(5);
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            Integer a = iterator.next();
            if (a == 2){
                list.remove(a);
            }
        }
       System.out.println(list); 
    }
}

  使用迭代器的remove()方法,则不会报错。 list.iterator()返回的Itr对象的remove方法维护了modCount和expectedModCount值。

        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            Integer a = iterator.next();
            if (a == 2){
                iterator.remove();
            }
        }

  Itr对象的remove方法源码如下所示:

       public void remove() {
            if (this.lastRet < 0) {
                throw new IllegalStateException();
            } else {
                this.checkForComodification();
                try {
                    ArrayList.this.remove(this.lastRet);
                    this.cursor = this.lastRet;
                    this.lastRet = -1;
                    this.expectedModCount = ArrayList.this.modCount;
                } catch (IndexOutOfBoundsException var2) {
                    throw new ConcurrentModificationException();
                }
            }
        }

  ListIterator类型的迭代器提供了add()方法和set()方法,支持List的新增和修改。

            ListIterator<Integer> iterator = list.listIterator();
            while (iterator.hasNext()){
                Integer a = iterator.next();
                if (a == 2){
                    iterator.add(66);
                }
            }
            //此时输出结果为[1, 2, 66, 2, 66, 4, 5]

4.4 foEach遍历(Jdk1.8新增)

      list.forEach(Integer->System.out.println(Integer));

4.5 通过keyset()遍历Map

  遍历时可通过map.put()方法修改,但是不能添加元素,否则会报java.util.ConcurrentModificationException异常错误。删除元素不会生效。

public class Test {
    public static void main(String[] args){
        HashMap<String,String> map = new HashMap<>();
        map.put("1","hello");
        map.put("2","world");
        map.put("3","get");
        
        for (String str:map.keySet()){
            //可修改,不可删除和添加
            System.out.println(map.get(str));
        }
    }

4.6 通过entrySet()方法遍历key和value

  使用该方式效率最高。

        for(Map.Entry<String,String> entry:map.entrySet()){
            System.out.println(entry.getKey()+entry.getValue());
        }

4.7 迭代遍历Map

        Iterator<Map.Entry<String,String>> iteratorEntry = map.entrySet().iterator();
        while (iteratorEntry.hasNext()){
            Map.Entry<String,String> entry = iteratorEntry.next();
            System.out.println(entry.getKey()+entry.getValue());
        }

4.8 通过values()方法遍历Map所有的value

        for (String value:map.values()){
            System.out.println(value);
        }

5. Java日志打印

      Logger logger  = LoggerFactory.getLogger(getClass());
      logger.info("info message!");

6.List和数组互相转化

        //数组转list-Arrays.asList
        String[] strArray = {"hello", "world"};
        List<String> list = Arrays.asList(strArray);
        for (String str : list) {
            System.out.println(str);
        }

        //list转数组-list.toArray
        String[] newStrArray = new String[list.size()];
        list.toArray(newStrArray);
        for (String str:newStrArray){
            System.out.println(str);
        }

7. 生成随机数

//随机生成1-10的整数
Random random = new Random();
int num = random.nextInt(10)+1;

8. 四舍五入

//四舍五入
int roundNum = (int) Math.round(num);
//向上取整
int ceilNum = (int) Math.ceil(num);
//向下取整
int floorNum = (int) Math.floor(num);

9. Eclipse & IDEA常用快捷键

01)打开类型:Ctrl+Shift+T
02)打开资源:Ctrl+Shift+R
03)打开搜索对话框: Ctrl+H
04)删除行:Ctrl+D
05)组织导入:Ctrl+Shift+O
06)定位在某行:Ctrl+L
07)定位到最后编辑的地方:Ctrl+Q
08)Debug模式下可以直接看到变量的值:Ctrl+Shift+I
09)快速显示当前类的继承结构:Ctrl+T
10)快速Outline:Ctrl+O
11) 重命名:Alt+Shift+R
12)上下移动代码:Alt+↑或↓
13)Surround With:Ctrl+Alt+T
14)自动导入依赖:Alt+Enter
15)导入重写、构造方法:Alt+Ins
16)显式继承关系图:Ctrl+Alt+U
17)跳转项目:Ctrl+Alt+]
18)文件跳转:Ctrl+E
19)Find Action:Ctrl+Shift+A
20)添加书签:Ctrl+Shfit+F11
21)添加断点:Ctrl+Shift+B
22)条件断点:Ctrl+Shift+F8
23)变量分析:Ctrl+U

将IDEAD的快捷方式设置为Eclipse模式:file->settings->keymap (eclipse),如图6-1所示:

图6-1 Eclipse模式设置.png

设置IDEAD忽略重复代码:file->settings->editor->inspections(General-Duplicated Code),如图6-2所示:

图6-2 设置忽略重复代码.png

设置IDEAD类注释模版:file->settings->editor->file and code templates(Class),如图6-3所示:

图6-3 设置类注释模版.png

IDEAD切换GIT远程分支:项目->git->repository->pull,点击刷新,如图6-4所示:

图6-4 IDEAD切换GIT远程分支.png

参考资料:
1)Java-String类型的参数传递问题
2)Java中的String为什么是不可变的? -- String源码分析
3)Java集合——遍历集合元素并修改
------<未完待续>------

相关文章

网友评论

      本文标题:Java常用技巧

      本文链接:https://www.haomeiwen.com/subject/ixskuctx.html