zoukankan      html  css  js  c++  java
  • Java笔记(四)常用基础类

    常用基础类

    一)String

    String内部用一个字符数组表示字符串:

    private final char value[];

    注意:Java9对此做了优化,采用byte[],如果字符都是ASCII字符,它就可以使用一个字节表示一个字符。

    String有的两个构造方法,可以根据参数创建String变量:

    //根据参数创建一个新的char数组,
    //并使用新的char数组实现String的大部分方法
    public String(char value[])
    public String(char value[], int offset, int count)

    编码相关:

    返回字符串给定编码的字节表示:

    public byte[] getBytes() //使用系统默认编码
    public byte[] getBytes(String charsetName)
    public byte[] getBytes(Charset charset)

    根据字节和编码创建字符串的构造方法:

    public String(byte bytes[], int offset, int length, String charsetName)
    public String(byte bytes[], Charset charset)

    不可变的String:

    String定义为不可变的程序可以更简单、安全、容易理解。

    常量字符串:

    Java中的字符串常量就是String对象,可以调用String的方法。

    在内存中它们被放入一个共享的地方,称为字符串常量池,它保存

    所有的常量字符串,每个常量只保存一份,被所有使用者共享。

    当通过常量的形式使用字符串的时候,就是使用常量池中的String类型对象。

            String a = "apple";
            String b = "apple";
            System.out.println(a == b); //true
    
            //上面的代码实际上相当于
            String c = new String(new char[]{'a', 'p', 'p', 'l', 'e'});
            String e = c;
            String d = c;
            //实际上只有一个String对象,三个变量都指向这个变量
            System.out.println(e == d); //true
    
            //注意:如果不是通过常量赋值,而是通过new创建,就会不一样
            String f = new String("apple");
            String g = new String("apple");
            System.out.println(f == g);//false 此时比较的是引用地址当然false

    因为,String的构造方法:

    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

    hash是String的一个实例变量,表示缓存的hashCode值

        public int hashCode() {
            int h = hash;
            if (h == 0 && value.length > 0) {
                char val[] = value;
    
                for (int i = 0; i < value.length; i++) {
                    h = 31 * h + val[i];
                }
                hash = h;
            }
            return h;
        }

    String与正则:

    public String[] split(String regex)
    public boolean matches(String regex)
    public String replaceFirst(String regex, String replacement)
    public String replaceAll(String regex, String replacement)

    二)StringBuilder

    StringBuffer是线程安全的而StringBuilder不是,但是注意线程安全是有成本的。

    1.基本实现原理

    StringBuilder父类AbstractStringBuilder封装了一个字符数组:

    char value[];

    与String不同的是,它不是final的可以被修改,它有一个实例变量表示数组中已经使用的字符个数:

    int count;

    抽象类的构造方法:

        AbstractStringBuilder(int capacity) {
            value = new char[capacity];
        }

    StringBuilder的构造方法:

    public StringBuilder() {
        super(16);
    }
        public StringBuilder(int capacity) {
            super(capacity);
        }
        public StringBuilder(String str) {
            super(str.length() + 16);
            append(str);
        }

    append方法:

        public AbstractStringBuilder append(String str) {
            if (str == null)
                return appendNull();
            int len = str.length();
           //确保数组的长度足以容纳新添加的字符
            ensureCapacityInternal(count + len);
            str.getChars(0, len, value, count);
            count += len;
            return this;
        }
        private void ensureCapacityInternal(int minimumCapacity) {
            // 如果数组长度小于需要的长度,则进行扩展
            if (minimumCapacity - value.length > 0) {
                value = Arrays.copyOf(value,
                        newCapacity(minimumCapacity));
            }
        }
    
        private int newCapacity(int minCapacity) {
            // overflow-conscious code
            int newCapacity = (value.length << 1) + 2;
            if (newCapacity - minCapacity < 0) {
                newCapacity = minCapacity;
            }
            return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
                ? hugeCapacity(minCapacity)
                : newCapacity;
        }

    注意:为了避免每次调用append方法就进行内存分配,指数扩展策略。

    在不知道最终需要多长(不知道下次append需要多长)的情况下

    ,指数扩展策略广泛应用于各种计算机内存分配相关的计算中。

    toString方法:

         public String toString() {
            // Create a copy, don't share the array
            return new String(value, 0, count);
        }

    String的 + 和 += 运算符:

    当String使用这两个运算符的时候,Java编译器一般会生成StringBuilder,

    并调用append方法。例如:

    String hello = "hello";
    hello+=",world";
    System.out.println(hello);
    //编译器会转换为:
    StringBuilder hello = new StringBuilder("hello");
    hello.append(",world");
    System.out.println(hello.toString());

    既然编译器调用了StringBuilder的append方法,那还有什么必要直接使用StringBuilder呢?

    因为在复杂的情况下编译器可能会生成过多的StringBuilder。

            String hello = "hello";
            for(int i=0;i<3;i++){
                hello+=",world";
            }
            System.out.println(hello);
    
            //编译器转换:
            /*String hello = "hello";
            for(int i=0;i<3;i++){
                StringBuilder sb = new StringBuilder(hello);
                sb.append(",world");
                hello = sb.toString();
            }
            System.out.println(hello);*/

    三)Arrays

    数组的优点是比容器的效率更高。

    Arrays类包含包含对数组操作的静态方法。

    1.toString

    输出一个数组的字符串形式,有9个重载方法:

    public static String toString(int[] a)
    public static String toString(Object[] a)

    2.排序

    sort()方法:

    //每总基本类型都有
    public static void sort(int[] a)
    public static void sort(double[] a)
    //引用类型:但对象要求实现Comparable接口
    public static void sort(Object[] a)
    public static void sort(Object[] a, int fromIndex, int toIndex)
    //或者
    public static <T> void sort(T[] a, Comparator<? super T> c)
    public static <T> void sort(T[] a, int fromIndex, int toIndex,Comparator<? super T> c)

    3.查找

    可在已排序的数组中进行二分查找(从中间开始找......):

    public static int binarySearch(int[] a, int key)
    public static int binarySearch(int[] a, int fromIndex, int toIndex, int key)
    public static int binarySearch(Object[] a, Object key)
    //自定义比较器,需要和排序时的比较器一致
    public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)

    5.多维数组

    本质:数组中的每个元素是另一个数组,层层嵌套。

    创建多维数组:

    int[][][] arr = new int[10][10][10];
    int[][] arr = new int[2][];
    arr[0] = new int[3];
    arr[1] = new int[5];

    Arrays数组中的方法都有针对多维数组的方法:

    public static String deepToString(Object[] a)
    public static boolean deepEquals(Object[] a1, Object[] a2)
    public static int deepHashCode(Object a[])

    四)日期和时间

    由于旧的API被广泛使用,这里介绍Java8之前的日期和时间API

    1.基本概念

    1)时区

    全球一共24个时区,英国格林尼治是0时区,北京是东八区,

    0时区也被称为GMT+0时间,GMT是格林尼治标准时间,北京时间就是GMT+8:00。

    2)时刻和纪元

    所有计算机内部都用一个整数表示时刻,这个整数是距离格林尼治标准时间1970年1月1日0时0分

    的毫秒数。这个时间也被称为Epoch Time(纪元时).这个整数表示的是一个时刻,与时区无关,世界

    上各个地方都是同一时刻,但各个地区对这一时刻的解读(如年月日时分)可能是不一样的。

    2.时间和日期API(Java8以前的)

    Date: 表示时刻,即绝对时间,与年月日无关。

    Calender:表示年历,Calender是一个抽象类,其中表示公历的子类为Gregorian-Calender。

    DateForm:表示格式化,能够将日期和时间与字符串间进行转换,该类也是一个抽象类,最

    常用的子类是SimpleDateForm。

    TimeZone:表示时区。

    Locale:表示国家(地区)和语言。

    1)Date

     内部用fast变量表示时刻:

    private transient long fastTime;//表示距离纪元时的毫秒数

    有两个构造方法:

        public Date() {
            this(System.currentTimeMillis());
        }
        public Date(long date) {
            fastTime = date;
        }

    Date中的大部分方法都过时,其中没有过时的有:

    public long getTime()//返回毫秒数
    public boolean equals(Object obj)//主要是比较内部毫秒数是否相同
    public int compareTo(Date anotherDate)//与其他毫秒数进行比较
    public boolean before(Date when)
    public boolean after(Date when)
    public int hashCode()

    2)TimeZone

            TimeZone defaultTimeZon = TimeZone.getDefault();
            System.out.println(defaultTimeZon.getID()); //Asia/Shanghai
    
            TimeZone usTimeZone = TimeZone.getTimeZone("US/Eastern");
            System.out.println(usTimeZone.getID()); //US/Eastern
            TimeZone unknownTimeZone = TimeZone.getTimeZone("GMT+09:00");
            System.out.println(unknownTimeZone.getID()); //GMT+09:00

    3)Locale

    它主要有两个参数:一个是国家或地区,一个是语言

    Locale类中定义了一些静态变量,表示常见的Locale,例如:

            System.out.println(Locale.US); //en_US
            System.out.println(Locale.ENGLISH); //en
            System.out.println(Locale.TAIWAN); //zh_TW
            System.out.println(Locale.CHINESE); //zh
            System.out.println(Locale.CHINA); //zh_CN
            System.out.println(Locale.SIMPLIFIED_CHINESE); //zh_CN

    4)Calendar

    该类是日期和时间操作的主要类,它表示与TimeZome和Locale相关的日历信息。

    Calendar内部也有一个表示时刻的毫秒数:

    protected long time;

    还有一个数组表示日历中各个字段的值

    protected int fields[];//这个数组长度为17,保存一个日期中各个字段的值

    Calendar定义了一些静态变量,表示这些字段如:

    Calendar.YEAR表示年

    Calender是抽象类,不能直接创建实例:

    public static Calendar getInstance()
    public static Calendar getInstance(TimeZone zone, Locale aLocale)
    //根据提供的TimeZone和Locale创建Calender子类对象

    内部,Calendar会将表示时刻的毫秒数,按照TimeZone和Locale对应的年历,

    计算各个日历字段的值,存放在fields数组中。

            Calendar calendar = Calendar.getInstance(Locale.CHINA);
            System.out.println("YEAR: " + calendar.get(Calendar.YEAR)); //2018
            System.out.println("MONTH: " + calendar.get(Calendar.MONTH)); //10
            System.out.println("DAY: " + calendar.get(Calendar.DAY_OF_MONTH)); //13
            System.out.println("Day of week: " + calendar.get(Calendar.DAY_OF_WEEK)); //3

    Calendar支持Date或者毫秒数设置时间:

    public final void setTime(Date date)
    public void setTimeInMillis(long millis)

    也可根据年月日设置:

    public final void set(int year, int month, int date)
    public final void set(int year, int month, int date,int hourOfDay, int minute, int second)
    public void set(int field, int value)

    直接增加或者减少时间:

    public void add(int field, int amount)//amount正数表示增加,负数表示减少

    Calendar之间也可以进行比较:

    public boolean equals(Object obj)
    public int compareTo(Calendar anotherCalendar)
    public boolean after(Object when)
    public boolean before(Object when)

    5)DateForm

    两个主要方法:

    public final String format(Date date)
    public Date parse(String source)

    DateForm定义了4个静态变量,表示4种风格,不同的风格输出详尽程度不一样。

    DateFrom是抽象类,用工厂方法创建实例:

    public final static DateFormat getDateTimeInstance()
    public final static DateFormat getDateInstance()
    public final static DateFormat getTimeInstance()

     重载方法接收日期及时间风格和Locale为参数:

    DateFormat getDateTimeInstance(int dateStyle, int timeStyle)
    DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)
            Calendar calendar = Calendar.getInstance();
            calendar.set(2018, 11, 15, 8, 22);
            System.out.println(DateFormat.getDateTimeInstance(DateFormat.LONG,
                    DateFormat.SHORT, Locale.CHINESE).format(calendar.getTime())); //2018年12月15日 上午8:22

    DateForm设置TimeZone:

    public void setTimeZone(TimeZone zone)

    6)SimpleDateForm

    该类是DateForm的子类,与父类的主要不同是,它可以接收一个自定义模式作为参数

            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());
            SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日  E HH时mm分ss秒");
            System.out.println("Now is : " + format.format(calendar.getTime()));
            //Now is : 2018年11月14日  星期三 09时31分29秒

    其中E表示星期,hh也可表示小时,不过是12小时制的。

    3.Java8之前时间和日期API的局限性

    1)Date种过时的方法

    2)Calendar操作繁琐

    API设计失败,要写很多代码,难以计算两个日期之间,比如有几天几个月等等。

    3)DateForm的线程安全问题

    五)随机

    1.Math.random

    该静态方法生成一个0到1的随机数(duoble类型),但不包括1和0.

            for (int i = 2; i >= 0; i--) {
                System.out.println(Math.random());
                /*0.2031695860558771
                0.7189188014572521
                0.686824037093907 */ //每次随机都不一样哦      
            }

    该方法的实现:

    public static double random() {
        Random rnd = randomNumberGenerator;
        if (rnd == null) rnd = initRNG();
        return rnd.nextDouble();
    }

    2.Random类

    该类提供了丰富的随机方法:

            Random random = new Random();
            System.out.println(random.nextInt());
            System.out.println(random.nextInt(100));
            //-809244560
            //87

    nextInt()产生一个随机的int,可能为正数,也可能为负数,nextInt(100)产生随机int,范围为0-100

    除了默认的构造方法还有一个接收long类型的种子参数的构造方法:

    public Random(long seed)

    种子决定了随机产生的序列,种子相同,产生的随机数序列就是相同的。

            Random random = new Random(20160824);
            for (int i = 0; i < 5; i++) {
                System.out.println(random.nextInt(100));
            }

    结果为:69  13  13  94  50

    这个程序无论执行多少遍,在哪儿执行,结果都是一样的。

    在Random类中还可用synchronized public void setSeed(long seed) 方法指定种子。

    指定种子是为了实现可重复随机。

    如果Random使用默认的构造函数,不传入种子,他会自动生成一个种子,

    这个种子数是真正的随机数。

    3.随机数的应用

    1)随机密码

        //生成6位数字随机密码
        public static String randomPassWord() {
            char[] chars = new char[6];
            Random random = new Random();
            for (int i = 0; i < 6; i++) {
                chars[i] = (char)(random.nextInt(10) + '0');
            }
            return new String(chars);
        }

    生成8位随机验证码,可能包含数字、特殊字符、字母

        private static final String SPECIAL_CHARS = "!@#$%^&*-=_+";
        private static char nextChar(Random rnd) {
            switch (rnd.nextInt(4)) {
                case 0:
                    return (char) ('a' + rnd.nextInt(26));
                case 1:
                    return (char) ('A' + rnd.nextInt(26));
                case 2:
                    return (char) ('0' + rnd.nextInt(10));
                default:
                    return SPECIAL_CHARS.charAt(rnd.nextInt(SPECIAL_CHARS.length()));
            }
        }
        //生成8位随机密码
        public static String randomPassWord() {
            char[] chars = new char[8];
            Random random = new Random();
            for (int i = 0; i < 8; i++) {
                chars[i] = nextChar(random);
            }
            return new String(chars);
        }
    Simple is important!
  • 相关阅读:
    回顾初心
    团队作业6(代码复审+事后诸葛亮)
    Alpha阶段项目复审(鸽牌开发小分队)
    事后诸葛亮(鸽牌开发小分队)
    团队作业5
    第七篇Scrum冲刺博客
    第六篇Scrum冲刺博客
    第五篇Scrum冲刺博客
    第四篇Scrum冲刺博客
    第三篇Scrum冲刺博客
  • 原文地址:https://www.cnblogs.com/Shadowplay/p/9953108.html
Copyright © 2011-2022 走看看