华清远见第三周学习总结

字符串String类中的常用方法

方法名返回值作用
length()int得到字符串的长度
toLowerCase()String转换为小写
toUpperCase()String转换为大写
trim()String去除字符串首尾的所有空格
isEmpty()boolean判断字符串是否为空白字符串""
getBytes()byte[]将字符串转换为字节数组
toCharArray()char[]将字符串转换为字符数组
equalsIgnoreCase(String str)boolean忽略大小写判断两个字符串是否相同
equals(String str)boolean判断两个字符串是否相同
charAt(int index)char得到字符串指定索引上的字符
indexOf(String str)int得到字符串中某个子字符串第一次出现的索引,如果不存在,返回-1
lastIndexOf(String str)int得到字符串中某个子字符串最后一次出现的索引,如果不存在,返回-1
contains(字符序列)boolean判断某个子字符串是否在原字符串中出现
concat(String str)String将参数字符串拼接到原字符串末尾
startsWith(String str)boolean判断是否以指定字符串开头
endsWith(String str)boolean判断是否以指定字符串结尾
substring(int begin)String从指定索引开始截取字符串至末尾
substring(int being,int end)String截取[begin,end)区间内的字符串
split(String regex)String[]按执行字符串或正则表达式切分原字符串。如果指定内容不再末尾,n个指定字符能得到n+1个子串;如果指定内容在末尾,n个指定字符能得到n个子串(不包含末尾的无效字符)
replace(char oldChar,char newChar)String将原字符串中的所有指定字符替换为新字符
String.valueOf(参数)String将任意参数转换为字符串。通常用于原始类型转换为字符串。
String.formart(String 格式,Object… obj)String根据指定格式转换参数。常用于将浮点数保留小数。如String.format(“%4.2f”,10.0/3)表示将计算的结果四舍五入保留2位小数转换为字符串;如果最终数据所占位置小于4,原样输出,大于4在最前补充空格。

StringBuilder类

用于表示可变字符串的一个类,是非线程安全的,建议在单线程环境下使用。

StringBuffer类

用于表示可变字符串的一个类,是线程安全的,建议在多线程环境下使用。

StringBuilder和StringBuffer中的方法都一致,只不过StringBuffer中的方法使用了synchoronized关键字修饰,表示是一个同步方法,在多线程环境下不会出现问题。

这里以StringBuilder为例

构造方法

常用构造方法作用
StringBuilder()创建一个大小为16的字符串数组,表示一个空白字符。类似于String str=“”;
StringBuilder(String str)创建一个str长度+16的字符数组后,将str添加到其中。类似于String str=“初始值”;

普通方法

常用方法作用
append(Object obj)将任意类型的参数添加到原可变字符串末尾
delete(int start,int end)删除[start,end)区间内的字符
deleteCharAt(int index)删除index索引上的字符
insert(int index,Object obj)在索引index上插入obj
replace(int start,int end,String str)将[start,end)区间内的字符替换为str
reverse()反转字符串

注意

  • 以上表格中的方法都是在直接操作同一个字符串对象,每次调用方法后,原字符串都会发生变化
  • StringBuffer和StringBuilder并没有重写equals方法,所以可变字符串的值是否相同时,调用的是equals中原始的==判断。如果要判断两个可变字符串的值是否相同时,需要将其转换为String后调用equals判断

System类

这个类中包含了一些系统相关的信息和一些方法。其中的属性和方法都是静态的。

该类不能创建对象,不是因为它是一个抽象类,而是因为它的构造方法是私有的。

常用属性和方法
System.out获取打印输出流PrintStream对象,用于控制台打印信息。
System.in获取输入流InputStream对象,用于获取输入的信息
System.err获取打印输出流PrintStream对象,用于控制台打印异常信息。
System.exit(int statues)终止虚拟机运行,参数0表示正常终止。
System.currentTimeMillis()获取从1970.1.1 0:0:0至今进过了多少毫秒。中国是UTC(+8),所以是从1970.1.1 8:0:0至今经过了多少毫秒。返回long类型。
System.arraycopy(原数组,原数组起始位置,目标数组,目标数组起始位置,原数组要复制的元素数量)将原数组中指定长度的元素复制到新数组中

RunTime类

Runtime类的对象,表示程序运行时对象(程序运行环境对象)。

包含了程序运行环境相关的信息。常用于获取运行环境信息(如虚拟机内存)或执行某个命令。

特点

这个类不是一个抽象类,但不能创建对象,因为它的构造方法是私有的。

这个类提供了一个静态方法getRuntime(),通过这个方法,可以获取一个Runtime类的对象。

这是Java中的一种设计模式–单例模式(一个类只能有一个创建对象)。

Date类

用于表示日期时间的类,位于java.util包下

构造方法

常用构造方法说明
Date()创建当前瞬间对应的日期对象
Date(long l)创建指定瞬间对应的日期对象
Date(int year,int month,int day)该构造方法已过时。创建指定年月日的日期对象(年是1900年起经过的年数,月用0-11表示1到12月)

常用方法

常用方法作用
getTime()得到对应Date对象表示的毫秒数
setTime(long l)设置Date对象的毫秒数
after(Date when)判断调用日期对象是否在when之后
before(Date when)判断调用日期对象是否在when之前

SimpleDateFormat类

用于格式化日期的类。

构造方法

常用构造方法作用
SimpleDateFormat(String pattern);创建一个指定日期模板的格式化日期对象

日期模板

特殊字符作用
yyyy年份
MM月份
dd日期
HH小时
mm分钟
ss
E星期
以上两个字母都可以写成一个,如月份5M:5,MM:05
yyyy/MM/dd HH:mm:ss E2022/11/24 16:24:09 星期四

常用方法

常用方法返回值作用
format(Date date)String将Date对象按日期模板转换为字符串
parse(String str)Date将满足日期模板的字符串转换为Date对象
package com.hqyj.test3;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatTest {
    public static void main(String[] args) throws ParseException {
        //定义格式化日期类所需的时间模板
        /*
        * yyyy  年
        * MM    月份
        * dd    日期
        * HH    24小时制
        * hh    12小时制
        * mm    分钟
        * ss    秒
        * E     星期
        *
        * 两个字母都可以写成一个,如月份MM和M
        * MM       5月实际为05
        * M       5月实际为5
        * */
        String patten = "yyyy/MM/dd HH:mm:ss E";//年/月/日 时:分:秒 星期
        //创建格式化日期类对象,参数为日期模板
        SimpleDateFormat sdf = new SimpleDateFormat(patten);
        //创建当前日期对象
        Date now = new Date();
        //调用格式化日期对象的format(Date date),将Date对象转换为指定日期格式的字符串
        String format = sdf.format(now);
        //输出
        System.out.println(format);
        //parse(String str)将指定日期模板的字符串转换为Date对象
        Date date = sdf.parse("2000/5/3 2:1:3 星期一");

        System.out.println(date);


    }
}

Calendar类

表示日历的类,包含了很多日历相关的信息。

是一个抽象类,无法创建对象。可以通过静态方法getInstance()获取该类的一个实例。

//获取Calendar类的对象
Calendar cal = Calendar.getInstance();

日历字段

在Calendar类中,定义了很多被final和static修饰的常量,称为日历字段,实际一个数字,用于获取指定信息

作用
Calendar.YEAR年份
Calendar.MONTH月份(0-11表示1-12月)
Calendar.DATE日期
Calendar.DAY_OF_WEEK星期(1-7表示周天到周六)
Calendar.HOUR12进制小时
Calendar.HOUR_OF_DAY24进制小时
Calendar.MINUTE分钟
Calendar.SECOND
Calendar.DAY_OF_MONTH本月第几天
Calendar.DAY_OF_YEAR本年第几天
Calendar.WEEK_OF_MONTH本月第几周
Calendar.WEEK_OF_YEAR本年第几周

常用方法

常用方法作用
get(int field)根据日历字段获取对应的值
getTime()获取对应的Date对象(Calendar对象转换为Date对象)
getMaximum(int field)获取指定日历字段支持的最大值,如Calendar.DATE最大31
getActualMaximum(int field)获取指定日历字段在当前日期下的实际最大值,如11月,Calendar.DATE最大30
set(int field,int value)将指定的日历字段设置为指定值
set(int year,int month,int date)同时设置日历对象的年月日
setTime(Date date)将Date对象作为参数设置日历对象的信息

实现万年历

package com.hqyj.homework;

import java.util.Calendar;
import java.util.Scanner;

public class Test2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入年月");
        Calendar cal = Calendar.getInstance();
        int year = sc.nextInt();
        int month = sc.nextInt();
        //设置指定年月,默认为1号
        cal.set(year, month - 1, 1);
        //用于换行的计数,每行7个,包括空格
        int count = 0;

        System.out.println("========"+year+"年"+month+"月========");
        System.out.println("一\t二\t三\t四\t五\t六\t日");

        //打印1号前的空格
        //DAY_OF_WEEK    星期     空格数量
        //2              一          0
        //3              二          1
        //4              三          2
        //5              四          3
        //6              五          4
        //7              六          5
        //1              日          6
        //获取本月1号是所在周的第几天
        int week = cal.get(Calendar.DAY_OF_WEEK);
        //根据规律判断,打印空格的同时也要计数
        if (week == 1) {
            System.out.print("\t\t\t\t\t\t");
            count += 6;
        } else {
            for (int i = 1; i <= week - 2; i++) {
                System.out.print("\t");
                count++;
            }
        }

        //遍历当月每一天
        for (int i = 1; i <= cal.getActualMaximum(Calendar.DATE); i++) {
            System.out.print(i + "\t");
            count++;
            //隔7换行
            if (count % 7 == 0) {
                System.out.println();
            }
        }

    }
}

Collection接口

该接口有两个核心子接口:List和Set。

这两个接口都可以保存一组元素,List接口保存元素时,是有序可重复的;Set接口保存元素时,是无序不重复的。

常用方法返回值作用
add(Object obj)boolean将元素添加到集合中
size()int获取集合中的元素数量
isEmpty()boolean判断集合是否为空
clear()void清空集合
contains(Object obj)boolean判断集合中是否存在指定元素
remove(Object obj)boolean移除集合中的指定元素
toArray()Object[]将集合转换为数组
iterator()Iterator获取集合的迭代器对象,用于遍历集合

List接口(有序可重复)

有序集合,元素可以重复,允许保存null,可以通过索引获取对应位置上的元素。

在该接口继承Collection接口的同时,又拓展了一些操作元素的方法,如添加到指定索引、根据索引删除、获取指定索引的元素、截取子集合的方法等。

常用方法返回值作用
get(int index)Object根据指定索引获取对应的元素
set(int index,Object obj)Object使用obj替换index上的元素,返回被替换的元素
add(int index,Object obj)void将obj添加到index上
remove(int index)Object移除指定索引的元素
indexOf(Object obj)int得到某元素第一次出现的索引,没有返回-1
lastIndexOf(Object obj)int得到某元素最后一次出现的索引,没有返回-1
subList(int from,int to)List截取[from,to)区间内的元素,返回子集合

ArrayList实现类(掌握)

  • 采用数组实现的集合
  • 可以通过索引访问元素,可以改变集合大小。如果要在其中插入或删除元素时,会影响后续元素
  • 该集合中保存的都是引用类型,即便保存了数组123,也保存的是Integer类型的123,而不是int类型的123
  • 该集合查询效率高,中途增加和删除元素效率低
构造方法
常用构造方法说明
ArrayList()创建一个Object类型的空数组。在调用添加方法后,才会更改该数组大小为10
ArrayList(int initialCapacity)创建一个指定容量的Object数组,如果参数为负,会抛出IllegalArgumentException异常
常用方法

ArrayList中的常用方法,就是Collection接口和List接口中定义的方法。

LinkedList实现类

  • 采用双向链表实现的集合
  • 集合中保存的每个元素也称为节点,除首尾节点外,其余节点都保存了自己的信息外,还保存了其前一个和后一个节点的地址
  • 如果在双向链表的数据结构中插入和删除操作节点时,不会影响其他节点的位置。如添加时新节点时,只需要重写定义新节点的前后节点位置即可
  • 如果要查询某个节点时,需要从头结点或尾结点开始一步步得到目标节点的位置
  • 双向链表在中间插入和删除的效率高,随机读取的效率低

img

构造方法
常用构造方法说明
LinkedList()创建一个空链表
常用方法

由于LinkedList既实现了List接口,又实现了Deque接口,所以还有Deque接口中的一些方法

实现Deque接口的方法说明
addFirst(Object obj)添加头元素
addLast(Object obj)添加尾元素
removeFirst()移除头元素
removeLast()移除尾元素
getFirst()得到头元素
getLast()得到尾元素
remove()移除头元素
pop()移除头元素
push(Object obj)添加头元素
peek()得到头元素
poll()移除头元素
offer(Object obj)添加尾元素

ArrayList和LinkedList的区别

  • 这两个类都是List接口的实现类,保存的元素有序可重复,允许保存null
  • ArrayList采用数组实现,随机读取效率高,插入删除效率低,适合用于查询
  • LinkedList采用双向链表实现,插入删除时不影响其他元素,效率高,随机读取效率低,适合用于频繁更新集合

Set接口(无序不重复)

无序集合,元素不可以重复,允许保存null,没有索引。

Set接口中没有自己定义的方法,都是继承于Collection接口中的方法

哈希表hash table

哈希表,也称为散列表,是一种数据结构,能更快地访问数据。

要保存的数据称为原始值,这个原始值通过一个函数得到一个新的数据,这个函数称为哈希函数,这个新数据称为哈希码,哈希码和原始值之间有一个映射关系,这个关系称为哈希映射,可以构造一张映射表,这个表称为哈希表。在哈希表中,可以通过哈希码快速地访问对应的原始值。

假设原本的数据为左侧的数组。

如果要查询10,需要遍历数组,效率不高。

通过一个特定的函数"原始值%5",得到一组新数据,让新数据重新对应元素,保存到“新数组”中,这个“新数组”称为哈希表。

这时如果要查询10,由于哈希函数是通过%5得到了0,所以直接查询哈希表中0对应的元素即可。

整个过程中,这个函数称为哈希函数,得到的新数据称为哈希码,新数组称为哈希表,对应关系称为哈希映射。

这个哈希函数,有一定的几率让多个原始值得到相同的哈希码,这种情况称为哈希冲突(哈希码一致,实际值不同),

为了解决哈希冲突,可以使用"拉链法",将2这个哈希码所在的位置向链表一样进行延伸。

哈希码的特点

  • 如果两个对象的hashCode不同,这两个对象一定不同
  • 如果两个对象的hashCode相同,这两个对象不一定相同
    • hashCode相同,对象不同,这种现象称为哈希冲突
    • **“通话""重地”**这两个字符串的hashCode相同,但是两个不同的对象

HashSet实现类

  • 采用哈希表实现
  • 元素不能重复,无序保存,允许保存一个null
  • 本质是一个HashMap对象
  • 使用HashSet集合时,通常要重写实体类中的equals和hashcode方法
构造方法
常用构造方法说明
HashSet()创建一个空集合,实际是创建一个HashMap对象。
常用方法

HashSet中没有属于自定义的方法,都是重写了父接口Set和Collection中的方法。这里参考Collection中的方法即可。

没有与索引相关的方法。

HashSet添加数据的原理

如果两个元素的hashCode相同且equals结果为true,视为同一个对象,不能添加。

每次向集合中添加元素时,先判断该元素的hashCode是否存在

  • 如果不存在,视为不同对象,直接添加
  • 如果存在,再判断equals方法的结果
    • 如果false,视为不同对象,可以添加
    • 如果true,视为同一对象,不能添加

由此可见,不能添加的条件是两个对象的hashCode相同且equals的结果为true。

如果每次只判断equals的话,由于equals方法通常重写时会判断很多属性,效率不高。

如果每次只判断hashCode的话,效率高,但有可能会有哈希冲突,

所以先判断hashCode,再判断equals,技能保证效率,又能保证不添加重复元素。

equals方法和hashCode的关系
  • 如果两个对象的equals方法结果为true,在没有重写equals方法的前提下,hashCode相同吗

    • 如果没有重写equals,默认是Object中使用==判断,如果结果为true,说明是同一个对象,hashCode一定相同
  • 如果两个对象的hashCode不同,在没有重写equals方法的前提下,equals方法的结果为?

    • hashCode不同,说明不是同一个对象,没有重写equals,说明使用Object中equals的==判断,结果为false
  • 如果两个对象的hashCode相同,equals方法的比较结果为?

    • 可能为true也可能为false

      String str1="hello";
      String str2="hello";
      //以上两个字符串使用同一个地址,hashCode相同,equals方法为true
      
      String str3="通话";
      String str4="重地";
      //以上连个字符串是不同地址,但hashCode相同,因为哈希冲突,equals方法为false
      
HashSet的应用

如果想要保存的对象保证不重复,且无关顺序,可以使用HashSet。如学生管理

Goods类

package com.hqyj.hashsetTest;

import java.util.Objects;

/*
 * 定义商品类
 * 品牌、名称、价格
 * */
public class Goods {
    private String brand;
    private String name;
    private int price;


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Goods goods = (Goods) o;
        return price == goods.price &&
                Objects.equals(brand, goods.brand) &&
                Objects.equals(name, goods.name);
    }

    /*
    * 根据所有属性生成哈希码
    * 如果两个对象的所有属性都一致,生成的哈希码就一致
    * */
    @Override
    public int hashCode() {
        return Objects.hash(brand, name, price);
    }

    @Override
    public String toString() {
        return "Goods{" +
                "brand='" + brand + '\'' +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }

    public Goods(String brand, String name, int price) {
        this.brand = brand;
        this.name = name;
        this.price = price;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

Main类

package com.hqyj.hashsetTest;

import java.util.HashSet;
import java.util.Iterator;

public class Main {
    public static void main(String[] args) {
        //创建一个HashSet集合
        HashSet<Goods> hs = new HashSet<>();
        //创建几个Goods对象
        //g1、g2、g3的属性不同,生成的hashcode不同,都能添加
        Goods g1 = new Goods("康师傅", "冰红茶", 3);
        Goods g2 = new Goods("康师傅", "红烧牛肉面", 5);
        Goods g3 = new Goods("农夫山泉", "矿物质水", 2);
        //g3与g4的属性相同,生成的hashcode相同,继续判断equals
        Goods g4 = new Goods("农夫山泉", "矿物质水", 2);

        //第一次添加,一定可以添加
        hs.add(g1);
        //第二次添加,对象的属性不同,hashcode不同,可以添加
        hs.add(g2);
        //第三次添加,对象的属性不同,hashcode不同,可以添加
        hs.add(g3);
        //第四次添加,对象的属性相同,hashcode相同,再判断equals结果,true,视为已存在,无法添加
        hs.add(g4);

        /*
         * HashSet没有可以通过索引获取对象的方法,所以无法使用普通for循环遍历
         * 这里可以使用增强for循环
         * */
        for (Goods g : hs) {
            System.out.println(g);
        }

        /*
         * 可以使用迭代器遍历HashSet集合中的所有元素
         * */
        /*Iterator<Goods> it = hs.iterator();
        while (it.hasNext()) {
            Goods goods = it.next();
            System.out.println(goods);
        }*/

    }
}

TreeSet实现类

  • 特殊的Set实现类,数据可以有序保存,可以重复,不能添加null
  • 采用红黑树(自平衡二叉树)实现的集合
    • 二叉树表示某个节点最多有两个子节点
    • 某个节点右侧节点值都大于左侧节点值
    • 红黑树会经过不停的"变色"、"旋转"达到二叉树的平衡
  • 只能添加同一种类型的对象且该类实现了Comparable接口
    • 实现Comparable接口后必须要重写compareTo()方法
    • 每次调用添加add(Object obj)方法时,就会自动调用参数的compareTo()方法
  • compareTo()方法的返回值决定了能否添加新元素和新元素的位置
    • 如果返回0,视为每次添加的是同一个元素,不能重复添加
    • 如果返回正数,将新元素添加到现有元素之后
    • 如果返回负数,将新元素添加到现有元素之前
  • 添加的元素可以自动排序
构造方法
常用构造方法说明
TreeSet()创建一个空集合,实际是创建了一个TreeMap对象
常用方法

属于Set的实现类,所以能使用Collection和Set中的方法,除此之外,还有独有的方法

常用方法作用
fisrt()得到集合中的第一个元素
last()得到集合中的最后一个元素
ceil(Object obj)得到比指定元素obj大的元素中的最小元素
floor(Object obj)得到比指定元素obj小的元素中的最大元素
TreeSet的应用

如果要保存的元素需要对其排序,使用该集合。

保存在其中的元素必须要实现Comparable接口,且重写compareTo()方法,自定义排序规则

流的分类

Java中将流定义为类,以对象的形式表现流。流有"四大家族",是所有流的父类。

字节输入流InputStream

FileInpuStreamObjectInputStream

字节输出流OutputStream

FileOutputStreamObjectOutputStream

字符输入流Reader

FileReader、BufferedReader、OutputStreamWriter

字符输出流Writer

FileWriter、BufferedWriter、InputStreamReader

按方向分类

  • 输入流:InputStream、Reader

    • 将硬盘中的数据读取到内存中
  • 输出流:OutputStream、Writer

    • 将内存中的数据写入到硬盘中

按类型分

  • 字节流:InputStream、OutputStream
    • 读写非文本类型文件。如图片、音视频、其他文件等。
  • 字符流:Reader、Writer
    • 读写纯文本类型文件。如txt、md等

如要将硬盘中某个txt文件中的内容读取到程序中,使用Reader

如要将硬盘中的某个图片读取到程序中,使用InputStream

如要将程序中的文本写入到硬盘中为txt类型文件时,使用Writer

如要将程序中的数据写入到硬盘中为非文本文件时,使用OutputStream

流的四个父类的特点

  • 这四个父类都是在java.io包下,都是抽象类,不能直接创建其对象,使用其子类创建对象
  • 这四个父类中都定义了close()方法,用于关闭流对象,释放资源
  • 输入流(InputStream和Reader)都有read()方法读取数据到内存中,输出流都有write()方法写入数据到硬盘中
  • 输出流(OutputStream和Writer)都有flush()方法,用于将流中的数据冲刷到硬盘中
    • 在使用输出流对象时,一定要调用flush()或close()方法后,才能真正将数据写入到硬盘中
  • 所有的流中,以Stream结尾,都是字节流,数据以字节传输;以Reader或Writer结尾的,都是字符流,数据以字符传输
  • 读取硬盘中的数据,使用输入流,读取的文件必须存在;将数据写入到硬盘中,使用输出流,文件可以不存在,但父目录必须存在。
  • 读入或写入文本时,使用字符流;读取或写入非文本时,使用字节流

FileInputStream文件字节输入流(掌握)

按字节读取硬盘中的文件。

构造方法

常用构造方法说明
FileInputStream(String pathName)根据文件名创建流对象
FileInputStream(File file)根据文件对象创建流对象

常用方法

常用方法说明
read()读取一个字节,返回读取到的字节
read(byte[] bytes)按字节数组读取,返回读取到的字节数量,读取到的内容保存在字节数组中
close()关闭流对象

FileOutputStream文件字节输出流(掌握)

按字节将内存中的数据写入到硬盘中。

构造方法

常用构造方法说明
FileOutputStream(String pathname)根据文件名创建输出流对象,写入时覆盖原内容
FileOutputStream(String pathname,boolean append)根据文件名创建输出流对象,第二个参数为true,写入时追加在原内容之后
FileOutputStream(File file)根据文件对象创建输出流对象,写入时覆盖原内容
FileOutputStream(File file,boolean append)根据文件对象创建输出流对象,第二个参数为true,写入时追加在原内容之后

常用方法

常用方法作用
write(int i)写入一个指定字节
write(byte[] bytes)写入一个字节数组
write(byte[] bytes,int off,int len)写入字节数组中从off开始的len个字节
flush()将流中的数据冲刷到硬盘中
close()关闭流对象

面试题

字符串相关面试题

//题目一
String str1 = "ab";//常量池中创建"ab"
String str2 = new String("ab");//堆中new String()保存常量池中已有的"ab"
String str3 = "a" + "b";//用常量池已有的"ab"
String str4 = "a" + new String("b");//常量池中创建"a"和"b",堆中new String()和new StringBuilder()
String str5 = "ab";//用常量池已有的"ab"

System.out.println(str1 == str2);//false
System.out.println(str1 == str3);//true
System.out.println(str1 == str4);//false
System.out.println(str1 == str5);//true


//题目二
//这两句话执行后,会创建几个对象
String s1 = "abc";
String s2 = "a" + "b" + "c";
//在字符串常量池中创建一个对象"abc"

//题目三
//这两句话执行后,会创建几个对象
String s3 = new String("你好");//常量池:"你好",堆中:new String()
String s4 = new String("你好");//堆中:new String()
//3个对象:堆中两个new String(),常量池中"你好"


//题目四
//这两句话执行后,会创建几个对象
String s5 = "hello";//常量池:"hello"
String s6 = "hel" + new String("lo");//常量池:"hel"和"lo"  堆:new String()和new StringBuilder
//5个对象:常量池:"hello"、"hel"和"lo",堆:new String()和new StringBuilder


//题目五
String s7 = new String("wor");//常量池:"wor",堆:new String()
String s8 = s7 + "ld";//常量池:"ld" 堆:new StringBuilder()
//4个对象:常量池:”wor"和"ld",堆:new String()和new StringBuilder

可变字符串面试题

比较String、StringBuilder和StringBuffer的区别

相同点:

  • 这三个类都可以表示字符串。都提供了一些操作字符串的方法。
  • 这三个类中有相同的方法,如charAt()、indexOf()等
  • 这三个类都是被final修饰的类,不能被继承

不同点:

  • String定义的字符串是一个常量。可变字符串定义的字符串是一个变量
  • String类中的方法,调用后,不会改变原本字符串的值;可变字符串类中的方法,调用后,会改变原本字符串的值
  • StringBuilder是非线程安全的可变字符串类,StringBuffer是线程安全的可变字符串类,其中的方法被synchronized修饰

总结

在频繁操作同一个字符串时,一定要使用可变字符串StringBuidler或StringBuffer类的对象,不能使用String类的对象

  • 什么叫死锁?怎么产生?如何解决?

    如果有两个人吃西餐,必须有刀和叉,此时只有一副刀叉。

    如果A拿到了刀,B拿到了叉,互相都在等待另一个工具,但都不释放自己拥有的,这时就会造成僵持的局面,这个局面就称为死锁,既不结束,也不继续。

模拟死锁出现的情况

定义两个线程类,线程A先获取资源A后,在获取资源B;线程B先获取资源B后,再获取资源A。

如果对资源A和资源B使用了synchronized进行同步,就会在线程A获取资源A的时候,线程B无法获取资源A,相反线程B在获取资源B的时候,线程A无法获取资源B,所以两个线程都不会得到另一个资源。

死锁的解决方式

方式一

让两个线程获取资源的顺序保持一致。

如两个线程都先获取knife,再获取fork

@Override
public void run() {
    synchronized (knife) {
        System.out.println(Thread.currentThread().getName() + "获取了knife,3s后获取fork");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (fork) {
            System.out.println(Thread.currentThread().getName() + "获取了fork,可以吃饭了");
        }
    }
}

方式二

让两个线程在获取资源A和B之前,再获取第三个资源,对第三个资源使用synchronized进行同步,这样某个线程在获取第三个资源后,将后续内容执行完毕,其他线程才能开始执行。

如在获取knife和fork之前,先获取paper对象

@Override
public void run() {
    //先获取paper,再进行后续操作
    synchronized (paper) {
        synchronized (knife) {
            System.out.println(Thread.currentThread().getName() + "获取了knife,3s后获取fork");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (fork) {
                System.out.println(Thread.currentThread().getName() + "获取了fork,可以吃饭了");
            }
        }
    }

以上大部分,都是老师上课的笔记,我摘抄了各种类的方法的使用,以及一些关键的面试题案例;

总结,这一章的学的效果并不是特别理想,有一部分是因为学的内容的的原因,还有一部分是因为自我产生了懈怠,下个星期把状态补回来,加油O.o!
n(Thread.currentThread().getName() + “获取了fork,可以吃饭了”);
}
}
}


### 方式二

让两个线程在获取资源A和B之前,再获取第三个资源,对第三个资源使用synchronized进行同步,这样某个线程在获取第三个资源后,将后续内容执行完毕,其他线程才能开始执行。

如在获取knife和fork之前,先获取paper对象

```java
@Override
public void run() {
    //先获取paper,再进行后续操作
    synchronized (paper) {
        synchronized (knife) {
            System.out.println(Thread.currentThread().getName() + "获取了knife,3s后获取fork");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (fork) {
                System.out.println(Thread.currentThread().getName() + "获取了fork,可以吃饭了");
            }
        }
    }

以上大部分,都是老师上课的笔记,我摘抄了各种类的方法的使用,以及一些关键的面试题案例;

总结,这一章的学的效果并不是特别理想,有一部分是因为学的内容的的原因,还有一部分是因为自我产生了懈怠,下个星期把状态补回来,加油O.o!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值