java1-基本数据类型/包装类-数组Array及Arrays-集合Collection/Map-大数值-atomic类-lock类-BlockingQueue类-Collections.synchronized方法

1,基本数据类型

1-1,数值型

  • 整型:byte(8bit)、short(16bit)、int(32bit)、long(64bit)
  • 浮点型:float(32bit)、double(64bit)

1-2,字符型:char

  • 字符型转int类型:

    int num = (int)var;
    
  • int类型转char类型:

    char var = (char)num;
    
  • 可以将字符直接赋予int类型的变量,或者将整数直接赋值给char类型:因为char类型在java中显示为字符,储存为数字。

    int num = '中';
    char var = 20013;
    
  • char chr = '2'+2;
    System.out.println(chr);
    //输出结果为4,是因为'2'的ascii码为50,50+2=52,'4'的ascii码为52,所以显示为'4'。
    

1-3,布尔型:boolean

  • 有两个常量值,在内存中占1bit,不可以使用0或非0的整数替代true和false

    public class TestVar06{
    	public static void main(String[] args){
    		boolean flag1 = true;
    		System.out.println(flag1);
    		boolean flag2 = false;
    		System.out.println(flag2);
    		boolean flag3 = 5==9;
    		System.out.println(flag3);
    	}
    }
    

1-4,布尔型:boolean

1-5,对应包装类

  • 包装类对应关系:
基本数据类型	包装类型
byte	java.lang.Byte(父类Number)
short	java.lang.Short(父类Number)
int	java.lang.Integer(父类Number)
long	java.lang.Long(父类Number)
float	java.lang.Float(父类Number)
double	java.lang.Double(父类Number)
boolean	java.lang.Boolean(父类Object)
char	java.lang.Character(父类Object)
  • 在 Java 5 中,引入了自动装箱和自动拆箱功能(boxing/unboxing),Java 可以根据上下文,自动进行转换,极大地简化了相关编程。
    • 自动装箱、拆箱发生在编译阶段,javac自动把装箱转换为Integer.valueOf(),把拆箱替换为Integer.intValue()
  • 关于 Integer 的值缓存,这涉及 Java 5 中另一个改进。构建 Integer 对象的传统方式是直接调用构造器,直接 new 一个对象。但是根据实践,我们发现大部分数据操作都是集中在有限的、较小的数值范围,因而,在 Java 5 中新增了静态工厂方法 valueOf,在调用它的时候会利用一个缓存机制,带来了明显的性能改进。按照 Javadoc,这个值默认缓存是 -128 到 127 之间。
    • 使用 valueOf 才能用到缓存,而 new 对象则不会。
    • 缓存机制并不是只有 Integer 才有,同样存在于其他的一些包装类,比如:
      • Boolean,缓存了 true/false 对应实例,确切说,只会返回两个常量实例 Boolean.TRUE/FALSE。
      • Short,同样是缓存了 -128 到 127 之间的数值。
      • Byte,数值有限,所以全部都被缓存。
      • Character,缓存范围’\u0000’ 到 ‘\u007F’。
    • Integer 缓存上限值实际是可以根据需要调整的,JVM 提供了参数设置:-XX:AutoBoxCacheMax=N
  • 原则上,建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,创建 10 万个 Java 对象和 10 万个整数的开销可不是一个数量级的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。我们其实可以把这个观点扩展开,使用原始数据类型、数组甚至本地代码实现等,在性能极度敏感的场景往往具有比较大的优势,用其替换掉包装类、动态数组(如 ArrayList)等可以作为性能优化的备选项。
  • 原始数据类型和 Java 泛型并不能配合使用,也就是Primitive Types 和Generic 不能混用,于是JAVA就设计了auto-boxing/unboxing机制,实际上就是primitive value 与 object之间的隐式转换机制,否则要是没有这个机制,开发者就必须每次手动显示转换,那多麻烦是不是?但是primitive value 与 object各自有各自的优势,primitive value在内存中存的是值,所以找到primitive value的内存位置,就可以获得值;不像object存的是reference,找到object的内存位置,还要根据reference找下一个内存空间,要产生更多的IO,所以计算性能比primitive value差,但是object具备generic的能力,更抽象,解决业务问题编程效率高。于是JAVA设计者的初衷估计是这样的:如果开发者要做计算,就应该使用primitive value;如果开发者要处理业务问题,就应该使用object,采用Generic机制;反正JAVA有auto-boxing/unboxing机制,对开发者来讲也不需要注意什么。然后为了弥补object计算能力的不足,还设计了static valueOf()方法提供缓存机制,算是一个弥补。

2,引用数据类型:

  • 类:class
  • 接口:interface
  • 数组

3,基本数据类型转换:

  • 在赋值运算或者算术运算的时候,要求数据类型一致,就要进行数据类型的转换。

  • 类型转换分为:自动转换、强制转换

  • 类型级别:

    • 由低到高:byte,short,char-->int-->long-->float-->double

    • 当一个表达式中有多种数据类型时,所有变量都会转换为级别最高的数据类型再进行计算

    • 进行运算时:

      • 左侧优先级与右侧相同:直接运算
      • 左侧优先级低于右侧:右侧强行转换
      • 左侧优先级高于右侧:右侧自动转换
      public class TestVar07{
      	public static void main(String[] args){
      		double d = 6;	//自动转换
      		System.out.println(d);
      		
      		//int e = 6.5;	//自动转换报错
      		//System.out.println(e);
      		
      		int e = (int)6.5;	//强制转换,该转换直接舍弃小数点后的数
      		System.out.println(e);
      		
      		/*
      		============================================================
      		在同一个表达式中,如果有多个数据类型时:
      		运算:整数、浮点数、字符型都可以参与运算,布尔型不可
      		============================================================
      		*/
      		//double d2 = 12+1294L+8.5f+3.81+'a'+true;
      		double d2 = 12+1294L+8.5f+3.81+'a';
      		System.out.println(d2);
              
              int i2 = (int) (12+1294L+8.5f+3.81+'a');
      		System.out.println(i2);
      	}
      }
      
    • 特殊情况:对于byte,short,char类型的变量,只要在他们表达的数值范围内,赋值的时候就不需要强行转换了,直接赋值/运算即可

      public class TestVar08{
      	public static void main(String[] args){
      		byte b = 12;
      		System.out.println(b);
      	}
      }
      /*
      默认12为int类型,优先级高于byte(-128,127),但是将12直接赋值给b并不会报错。
      */
      

4,键盘输入及final关键字

  • final: 一个变量被final修饰,就成为了一个常量,值不可再变;这个常量就是符号常量(字符常量,区别为字面常量);一般字符常量的名字全部大写。

    import java.util.Scanner;
    
    public class TestVar09{
    	public static void main(String[] args){
    		//提取变量
    		final double PI = 3.1415926;
    		
    		//扫描器
    		System.out.print("请输入半径:");
    		Scanner sc = new Scanner(System.in);
    		int r = sc.nextInt();
    		
    		double c = 2 * PI * r;
    		System.out.println("周长为:"+c);
    		double s = PI * r * r;
    		System.out.println("面积为:"+s);
    		
    		//PI = 9;	//报错
    	}
    }
    
  • 输入:Scanner 与字符串:String(charAt转为字符型)

    import java.util.Scanner;
    
    public class TestVar10{
    	public static void main(String[] args){
    		Scanner sc = new Scanner(System.in);
    		System.out.print("请输入年龄:");
    		int age = sc.nextInt();
    		
    		System.out.print("请输入身高:");
    		double height = sc.nextDouble();
    		
    		System.out.print("请输入姓名:");
    		String name = sc.next();
    		
    		System.out.print("请输入性别:");
    		char sex = sc.next().charAt(0);
    		System.out.println("性别为:"+sex);
    	}
    }
    

5,数组

5-1,数组的基本概念及操作(声明、创建/初始化、赋值、初始化)

  • 数组用来存储数据,可以将相同类型的若干数据组织起来,将这个若干数据集合称为数组。

  • 数组的使用:

    • 声明(定义数组)

      int[] arr;
      int arr[];
      
    • 创建

      arr = new int[4];
      
      //声明和创建同时进行
      int[] arr = new int[4];
      
    • 赋值

      arr[0] = 1;
      arr[1] = 2;
      ...
      
    • 使用

      System.out.println(arr[0])
      System.out.println(arr.length)	//获取长度
      System.out.println(arr)	//首地址
      
  • 数组的初始化

    //静态初始化,不可分开写
    int[] arr = {12,34,56};
    int[] arr = new int[]{12,34,56};
    //动态初始化
    int[] arr = new int[3];	//这一步可以分开 int[] arr; arr = new int[3;]
    arr[0] = 12;
    arr[1] = 34;
    arr[2] = 56;
    //默认初始化
    int[] arr = new int[3];	//数字类型的数组都是对应的0,char为'\u0000',boolean为false
    //二维初始化
    int[][] arr = {{-1,0}, {1,0}, {0,-1}, {0,1}};
    
  • java数组的特点:

    • 长度是确定的,一旦被创建,长度不可改变
    • 元素必须是相同类型,不允许出现混合类型
    • 数组类型可以是任意数据类型:基本类型,引用类型
    • 数组变量属于引用类型,数组也是对象,在堆中存储

5-2,数组的增删改查

  • //定长数组
    for(int i=arr.length-1;i>=(index+1);i--){
        arr[i] = arr[i-1];
    }
    arr[index] = target;
    
    //元素不丢失
    int[] arr2 = new int[arr.length+1];
    for (int i=0;i<arr.length;i++){
        if(i<index){
            arr2[i] = arr[i];
        }else if(i>index){
            arr2[i] = arr[i-1];
        }
    }
    arr2[index] = target;
    
    • 删除指定位置的元素

      for(int i=index;i<arr.length;i++){
          arr[i] = arr[i+1];
      }
      arr[arr.length-1] = 0;
      
    • 删除指定的元素

      先由元素获取索引
      再删除指定位置的元素
      
  • arr[index] = target;
    
    • 由索引获取元素

      num = arr[索引]
      
    • 由元素获取索引

      //遍历;
      int index = -1;	//初始化为数组不包含的索引,防止有的元素不在数组中
      for(int i=0;i<arr.length;i++){
          if(arr[i]==num){
              index = i;
              break;
          }
      }
      

5-3,Arrays工具类

  • 常用方法

    Arrays.toString(arr)	//将数组转为字符串
        
    Arrays.sort(arr)		//对数组进行升序排列
    Arrays.sort(arr, Colletions.reverseOrder())	//反向排序
    Arrays.sort(arr, (a,b)->(b-a))	//使用匿名类进行反向排序
        
    Arrays.binarySearch(arr, key)	//使用二分查找对排序后的数组寻找key的索引,数组在调用前必须排序好的,如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。(来自菜鸟教程:https://www.runoob.com/java/java-array.html)
        
    Arrays.copyOf(arr, length)		//复制length长度的arr为一个新数组
    Arrays.copyOfRange(arr, i, j)	//赋值arr从i到j的范围为一个新数组,顾头不顾尾
        
    Arrays.equals(arr1, arr2)		//比较两个数组的值是否相同
        
    Arrays.fill(arr, i, index1, index2)				//使用i将arr填充
    

5-4,数组复制

System.arraycopy(src, srcPos, dest, destPos, length)
//src		源数组
//srcPos	源数组起始位置
//dest		目标数组
//destPos	目标数组起始位置
//length	要复制的长度

5-5,二维数组的定义,遍历,初始化

public class Test09{
	public static void main(String[] args){
        //定义
		int[][] arr = new int[3][];
		arr[0] = new int[]{1,2,3,4};
		arr[1] = new int[]{2,3,4,5};
		arr[2] = new int[]{3,4,5,6};
		
        //遍历,内层外层都可以用普通和增强遍历
		for(int[] arrMiddle:arr){
			System.out.println("======");
			for(int num:arrMiddle){
				System.out.println(num);
			}
		}
	}
}
  • 初始化

    //静态初始化,除了用new关键字产生数组以外,可以直接在定义数组的同时为数组元素分配空间并赋值
    int[][] arr = {{1,2},{2,3},{3,4}};
    int[][] arr = new int[][]{{1,2},{2,3},{3,4}};
    //动态初始化
    int[][] arr = new int[3][];	//本质上定义一个一维数组,长度为3,每个位置可以放入一个数组
    arr[0] = {1,2,3,4};
    arr[1] = {2,3,4,5};
    arr[2] = {3,4,5,6};
    
    int[][] arr = new int[3][2];//本质上定义一个一维数组,长度为3,每个位置可以放入一个长度为2的数组
    
    //默认初始化
    int[][] arr = new int[3][2];	//数字类型的数组都是对应的0,char为'\u0000',boolean为false
    

5-6,数组的使用例子

例子1
import java.util.Scanner;

public class Test02{
	public static void main(String[] args){
		int[] grades = new int[10];
		
		int sum = 0;
		Scanner sc = new Scanner(System.in);
		
		for (int i=0; i<10; i++){
			System.out.print("请输入第"+i+"个学生的成绩:");
			grades[i] = sc.nextInt();
			sum += grades[i];
		}
		
		System.out.println("学生的总成绩是:"+sum);
		System.out.println("学生的平均成绩是:"+sum/grades.length);
		System.out.println("各学生的成绩是:");
		System.out.println(grades);	//这样打印出来的只是数组的首地址
		for(int grade:grades){
			System.out.println(grade);
		}
		
		while(true){
			System.out.println("请输入你要查询的学生的序号,退出请输出Q");
			if (!sc.hasNextInt()){
				//此时输入为字符
				if(sc.next().charAt(0)=='Q'){
					//退出
					break;
				}else{
					System.out.println("违法输入!!!");
				}
			}else{
				int order = sc.nextInt() - 1;
				System.out.println("该生成绩为:"+grades[order]);
			}
		}
	}
}
例子2
public class Test03{
	public static void main(String[] args){
		int[] arr = {1,2,3,4,5,6,7,8,9};
		int minNumber = Test03.getMinNumber(arr);
		int maxNumber = Test03.getMaxNumber(arr);
		System.out.println("最小值为:"+minNumber);
		System.out.println("最大值为:"+maxNumber);
	}
	
	public static int getMaxNumber(int[] arr){
		int maxNumber = arr[0];
		for (int num:arr){
			if(num>maxNumber){
				maxNumber = num;
			}
		}
		return maxNumber;
	}
	
	public static int getMinNumber(int[] arr){
		int minNumber = arr[0];
		for (int num:arr){
			if(num<minNumber){
				minNumber = num;
			}
		}
		return minNumber;
	}
}

6,Collection及Map

  • 继承关系
    • Collection <- List <- ArrayList,LinkedList //最大特点,存储的元素有序(插入时的顺序)且可重复
    • Collection <- Set <- HashSet //最大特点,存储的元素无序且不可重复
    • Collection <- Queue <- ArrayDeque,LinkedList //最大特点,存储的元素有序(特定规则下的顺序,如优先级队列:PriorityQueue)且可重复
    • Map <- HashTable //Map的最大特点,键值对的映射
    • Map <- HashMap <- LinkedHashMap
  • Collection接口定义有toArray,可以将List,Set,Queue转换为数组
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> test = new ArrayList<>();
        for(int i=0; i<10; i++){
            test.add(i);
        }
        
        Integer[] test1 = new Integer[test.size()];
        test.toArray(test1);
        System.out.println(test1[0]);
        System.out.println(test1[0].getClass());
        
        Object[] test2 = test.toArray();
        System.out.println(test2[0]);
        System.out.println(test2[0].getClass());
        
    }
}

7,大数值

  • 如果基本的整数和浮点数精度不能够满足需求,那么可以使用java.math包中的两个很有用的类:BigIntegerBigDecimal,这两个类可以处理包含任意长度数字序列的数值。
    • BigInteger类实现了任意精度的整数运算
    • BigDecimal类实现了任意精度的浮点数运算。
  • 使用静态valueOf方法可以将普通的数值转化为大数值
    public class test3 {
        public static void main(String[] args) throws IOException {
            BigInteger num1 = BigInteger.valueOf(123);
            System.out.println(num1.getClass());
        }
    }
    
  • 不能使用人们熟悉的算术运算符(如:+*)处理大数值。而需要使用大数值类中的addmultiply方法
    //大整数
    public class test3 {
        public static void main(String[] args) throws IOException {
            BigInteger num1 = BigInteger.valueOf((long)1e18);
            BigInteger num2 = BigInteger.valueOf((long)1e18);
            System.out.println(num1.add(num2));
            System.out.println((long)1e18+(long)1e18);
            System.out.println(num1.subtract(num2));
            System.out.println((long)1e18-(long)1e18);
            System.out.println(num1.multiply(num2));
            System.out.println((long)1e18*(long)1e18);
            System.out.println(num1.multiply(num2).divide(BigInteger.TWO));
            System.out.println((long)1e18*(long)1e18/2);
            System.out.println(num1.multiply(num2).mod(BigInteger.TWO));
            System.out.println((long)1e18*(long)1e18%2);
            System.out.println(num1.compareTo(num2));
            System.out.println(num2.compareTo(num1));
            System.out.println(num1.compareTo(num1));
        }
    }
    
    //大实数
    public class test3 {
        public static void main(String[] args) throws IOException {
            BigDecimal num1 = BigDecimal.valueOf(3.3333333333);
            BigDecimal num2 = BigDecimal.valueOf(2.222222222);
            System.out.println(num1.multiply(num2).multiply(num1).multiply(num1));
            System.out.println(num1.divide(num2, RoundingMode.HALF_UP));    //除法需要制定舍入方式
            double num3 = 3.3333333333;
            double num4 = 2.222222222;
            System.out.println(num3*num4*num3*num3);
            System.out.println(num3/num4);
        }
    }
    

8,线程安全基础类型-atomic类

  • 原始数据类型的变量,要使用并发相关手段,才能保证线程安全;如果有线程安全的计算需要,建议考虑使用类似 AtomicInteger、AtomicLong 这样的线程安全基础类型。
  • java1.5加入了Atomic这个帮助类型,位于java.util.concurrent.atomic。这里的atomic是线程安全的基本数据类型。
  • atomic系列包含以下数据类型
AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicIntegerFieldUpdater
AtomicLong
AtomicLongArray
AtomicLongFieldUpdater
AtomicMarkableReference
AtomicReference
AtomicReferenceArray
AtomicReferenceFieldUpdater
AtomicStampedReference
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
  • Java线程安全基础类型
  • AtomicInteger如何保证线程安全?
    • 用volatile关键字修饰value字段:AtomicInteger用value字段来存储数据值,volatile关键字保证了value字段对各个线程的可见性。各线程读取value字段时,会先从主内存把数据同步到工作内存,这样保证可见性。
    • Unsafe实现操作原子性,用户在使用时无需额外的同步操作:如AtomicInteger提供了自增方法getAndIncrement,其内部实现是由Unsafe类的compareAndSetInt方法来保证的。
      • compareAndSetInt方法是一个CAS操作,用native关键字修饰。原理:先比较内存中的值与expected是否一致,一致的前提下才赋予新的值x,此时返回true,否则返回false。
    • 总之,AtomicInteger的线程安全由两点保证:
      • volatile关键字修饰value字段,保证value字段对各个线程的可见性;
      • CAS操作保证了多线程环境下对数据修改的安全性。
      • AtomicLong/AtomicBoolean等均为相同的原理。
  • 例子
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class TestAtomicInteger {

    public static void main(String[] args) {
        TestAtomicInteger test = new TestAtomicInteger();
        test.test();
    }

    private static int THREAD_COUNTER = 10;
    private CountDownLatch countDownLatcher = new CountDownLatch(THREAD_COUNTER);
    private Integer a = Integer.valueOf(0);
    private AtomicInteger atomicA = new AtomicInteger(0);

    private class CounterRunnable implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
                // Integer
                a++;
                // AtomicInteger
                atomicA.getAndIncrement();
            }

            countDownLatcher.countDown();
        }

    };

    private void test() {
        CounterRunnable r = new CounterRunnable();
        for (int i = 0; i < THREAD_COUNTER; i++) {
            Thread thread = new Thread(r);
            thread.start();
        }

        try {
            countDownLatcher.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("a:       " + a);
        System.out.println("atomicA: " + atomicA);
    }

}

9,Lock类

10,BlockingQueue类

  • BlockingQueue阻塞队列体系继承体系
    • https://www.cnblogs.com/tjudzj/p/4454490.html
    • 在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。
    • BlockingQueue的两个常见阻塞场景:
      • 当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。
      • 当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有空的位置,线程被自动唤醒。
      • 这也是我们在多线程环境下,为什么需要BlockingQueue的原因。作为BlockingQueue的使用者,我们再也不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了。
    • 其实BlockingQueue阻塞队列体系的继承(实现)层次比较简单,我们只要需要先学习一下BlockingQueue中的方法:
    public interface BlockingQueue<E> extends Queue<E> {
        void put(E e) throws InterruptedException;--插入一个元素,满了就等待
        E take() throws InterruptedException;--弹出队首元素,空了就等待
    
        boolean add(E e);--往队列中插入一个对象,队列满了会抛异常,满了不会等待
        boolean offer(E e);--同上,区别是队列满了会返回false
        boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;--规定时间内插入元素
    
        E poll(long timeout, TimeUnit unit) throws InterruptedException;--规定时间内弹出队首,空了不会等待
    
        int remainingCapacity();--队列剩余大小
        boolean remove(Object o);--删除一个equals o的对象
        public boolean contains(Object o);--是否包含o
        int drainTo(Collection<? super E> c);--把队列迁移到另外一个collection结构中
        int drainTo(Collection<? super E> c, int maxElements);--迁移,有个最大迁移数量
    }
    
    • 下面去看一下具体的几个实现类。
      • ArrayBlockingQueue--声明时就确定大小的队列,fifo方式。(方法基本和接口一致,没有特别要说明的内容)
      • LinkedBlockingQueue--链表实现的queue-remove效率会高一些。作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。
      • PriorityBlockingQueue--优先级队列,PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。在实现时,内部控制线程同步的锁采用的是公平锁。
      • SynchronousQueue--阻塞队列,必须拿走一个才能放进来一个,也就是最多只有一个~
      • DelayQuque--就是放进去的内容,延迟时间到了后才可以获得,DelayQueue是一个没有大小限制的队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。
      • LinkedBlockDeque--双端队列 :offerFirst/offerLast,pollFirst/pollLast
      • LinkedTransferQueue--类似LinkedUnBlockedQueue,其实就是transfer方法有人再等待队列内容就直接给他这个元素,没人在等就放在队列里面。也就是效率会更高。

11,Collections.synchronized方法

  • Collection/Map的集合类都不是线程安全的,但是并不代表这些集合完全不能支持并发编程的场景。在 Collections 工具类中,提供了一系列的 synchronized 方法,我们完全可以利用类似方法来实现基本的线程安全集合:
List list = Collections.synchronizedList(new ArrayList());
  • 它的实现,基本就是将每个基本方法,比如 get、set、add 之类,都通过 synchronized 添加基本的同步支持,非常简单粗暴,但也非常实用。注意这些方法创建的线程安全集合,都符合迭代时 fail-fast 行为,当发生意外的并发修改时,尽早抛出 ConcurrentModificationException 异常,以避免不可预计的行为。

N,不同类型的变量之间的相互转换

num + ""                        int->String
Interge.parseInt(String)     String->int
Integer.valueOf(String)      valueOf(String)方法会返回Integer类的对象,而parseInt(String)方法返回原始的int值。

char - '0'                        char->int(也可隐式转换)
(int)char

str.toCharArray()                String->char[]
posted @ 2021-08-18 09:08  tensor_zhang  阅读(132)  评论(0编辑  收藏  举报