笔记-狂神说Java-API,集合, IO

常用API

date

    // System.exit(0);//强制退出java程序非0是异常结束
       //long l = System.currentTimeMillis();//返回当前时间与1970年子午夜交替毫秒值
//       Date date = new Date();//返回具体日期时间
//       DateFormat dateInstance = DateFormat.getDateInstance();//获取日期格式
//       DateFormat dateTimeInstance = DateFormat.getDateTimeInstance();//获取具体日期时间
//       String s = dateInstance.format(date);
//       String s1 = dateTimeInstance.format(date);
//       System.out.println(s);
//       System.out.println(s1);
       String s="2020/4/4 22:22:22";
       //编写对应日期格式,将字符串日期转换为日期格式
       SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
       Date parse = simpleDateFormat.parse(s);
       System.out.println(parse);

 

math

int abs = Math.abs(-5);
Math.sqrt(1000.0);
Math.max(1000,2000);
Math.min(1000,2000);
Math.round(1000.6f);
Math.random();
Random random = new Random();
int i = random.nextInt(100);
System.out.println(i);

 

BigInteger类

BigInteger b1 = new BigInteger("6666666666");
BigInteger b2 = new BigInteger("2222222222");
//加、减、乘、除、取模
System.out.println(b1.add(b2));
System.out.println(b1.subtract(b2));
System.out.println(b1.multiply(b2));
System.out.println(b1.divide(b2));
System.out.println(b1.remainder(b2));

BigDecimal类

BigDecimal b3 = new BigDecimal("15.3");
BigDecimal b4 = new BigDecimal("3.0");
System.out.println(b3.divide(b4,5,BigDecimal.ROUND_HALF_UP));
//除法除不尽可以在后面加,保留几位小数 ,属性(四舍五入)

克隆技术

类要实现克隆,需要做两步:

  1. 实现Cloneable接口;

  2. 重写Object的clone方法。

    Timer定时器

    Timer timer = new Timer();
    TimerTask task = new TimerTask() {

       public void run() {
           System.out.println("定时任务被执行了");
      }
    };
    //一段时间后执行定时任务
    timer.schedule(task,2000);
    //在具体时间执行任务
    timer.schedule(task,new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse("2020/5/18 14:56:10"));
    //在一段时间后执行任务,并且每隔多久执行一次
    timer.schedule(task,2000,3000);

    Calendar

    常用成员方法

    public int get(int field):返回给定日历字段的值。
    public void set(int field,int value):将给定日历字段设置给定值。
    public abstract void add(int field,int amount):根据日历的规则,为给定的日历字段添加或减去指定的时间量。
    public Date getTime();把日历时间值转换为date对象

     

    String

    String:字符串,使用一对“”引起来表示

    1. String声明为final的,不可被继承

    2. String实现了Serializable接口:表示字符串是支持序列化的

    3. 实现了comparable接口:表示String可以比较大小

    4. String内部定义了final char【】 value用于存储字符串数据

    5. String:代表不可变的字符序列:简称:不可变性。

      体现:当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值

    6. 通过字面量的方式给一个字符串赋值,此时的字符串值声明在字符串常量池中,字符串常量池中是不会存储相同内容的字符串。

      基本类型与字符串之间的转换

      基本类型--》字符串

      1.基本类型—+“”,

      2.包装类的静态方法tostring(参数),不是Object的toString重载,static String toString(int i)返回一个指定整数的String对象

      3.String类的静态方法valueof(参数)

      字符串-》基本类型

      使用包装类大家平台方法parsexxx(“数值类型的字符串”)

      //基本类型转换字符串
      Integer i=1;
      String s=i+"";
      String s1=Integer.toString(1);
      System.out.println(i.toString());
      System.out.println(s1+200);
      String s2=String.valueOf(i);
      //字符串转换基本类型
      int i1=Integer.parseInt(s1);
      //字符串的操作
                 String s="猫猫猫狗猫猫猫猫猫猫狗猫猫猫猫猫狗猫猫猫猫猫猫猫";
                 //字符串检索
                 //检索字符串是否有该字符,返回Boolean
                 System.out.println(s.contains("狗"));//有返回true
                 //检索字符串是否存在,存在返回位置下标,不存在返回-1
                 System.out.println(s.indexOf("狗"));//存在返回位置下标
                 //检索字符串开始位置是否为该字符
                 System.out.println(s.startsWith("猫"));//返回Boolean
                 //检索字符串结尾位置是否为该字符
                 System.out.println(s.endsWith("猫"));//返回Boolean

                 //字符串替换
                 System.out.println(s.replace("狗","鼠"));
                 //字符串拆分以该字符拆分并输出0下标的字符
                 System.out.println(s.split("狗")[0]);
                 //字符的长度
                 System.out.println(s.length());
                 //字符是否为空
                 System.out.println(s.isEmpty());
                 

      StringBuilder

      StringBuilder su=new StringBuilder();
      String s=su.toString();

      集合

      image-20200519152612018

       

       

重点:HashMap

①HashMap的工作原理

HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。

当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。

②HashMap和Hashtable的区别

HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。

  1. HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。

  2. HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

  3. 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。

  4. 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。

  5. HashMap不能保证随着时间的推移Map中的元素次序是不变的。

要注意的一些重要术语:

1) sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。

2) Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。

3) 结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。

我们能否让HashMap同步?

HashMap可以通过下面的语句进行同步: Map m = Collections.synchronizeMap(hashMap);

结论

Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。

ashMap和HashSet的区别是Java面试中最常被问到的问题。如果没有涉及到Collection框架以及多线程的面试,可以说是不完整。而Collection框架的问题不涉及到HashSet和HashMap,也可以说是不完整。HashMap和HashSet都是collection框架的一部分,它们让我们能够使用对象的集合。collection框架有自己的接口和实现,主要分为Set接口,List接口和Queue接口。它们有各自的特点,Set的集合里不允许对象有重复的值,List允许有重复,它对集合中的对象进行索引,Queue的工作原理是FCFS算法(First Come, First Serve)。

首先让我们来看看什么是HashMap和HashSet,然后再来比较它们之间的分别。

③HashMap和HashSet的区别

HashMap和HashSet的区别是Java面试中最常被问到的问题。如果没有涉及到Collection框架以及多线程的面试,可以说是不完整。而Collection框架的问题不涉及到HashSet和HashMap,也可以说是不完整。HashMap和HashSet都是collection框架的一部分,它们让我们能够使用对象的集合。collection框架有自己的接口和实现,主要分为Set接口,List接口和Queue接口。它们有各自的特点,Set的集合里不允许对象有重复的值,List允许有重复,它对集合中的对象进行索引,Queue的工作原理是FCFS算法(First Come, First Serve)。

首先让我们来看看什么是HashMap和HashSet,然后再来比较它们之间的分别。

什么是HashSet

HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果我们没有重写这两个方法,将会使用这个方法的默认实现。

public boolean add(Object o)方法用来在Set中添加元素,当元素值重复时则会立即返回false,如果成功添加的话会返回true。

什么是HashMap

HashMap实现了Map接口,Map接口对键值对进行映射。Map中不允许重复的键。Map接口有两个基本的实现,HashMap和TreeMap。TreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为null。HashMap是非synchronized的,但collection框架提供方法能保证HashMap synchronized,这样多个线程同时访问HashMap时,能保证只有一个线程更改Map。

public Object put(Object Key,Object value)方法用来将元素添加到map中。

HashSet和HashMap的区别

HashMapHashSet
HashMap实现了Map接口 HashSet实现了Set接口
HashMap储存键值对 HashSet仅仅存储对象
使用put()方法将元素放入map中 使用add()方法将元素放入set中
HashMap中使用键对象来计算hashcode值 HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false
HashMap比较快,因为是使用唯一的键来获取对象 HashSet较HashMap来说比较慢

 

 

④面试题

HashMap的工作原理是近年来常见的Java面试题。几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hashtable和HashMap之间的区别,那么为何这道面试题如此特殊呢?是因为这道题考察的深度很深。这题经常出现在高级或中高级面试中。投资银行更喜欢问这个问题,甚至会要求你实现HashMap来考察你的编程能力。ConcurrentHashMap和其它同步集合的引入让这道题变得更加复杂。让我们开始探索的旅程吧!

“你用过HashMap吗?” “什么是HashMap?你为什么用到它?”

几乎每个人都会回答“是的”,然后回答HashMap的一些特性,譬如HashMap可以接受null键值和值,而Hashtable则不能;HashMap是非synchronized;HashMap很快;以及HashMap储存的是键值对等等。这显示出你已经用过HashMap,而且对它相当的熟悉。但是面试官来个急转直下,从此刻开始问出一些***钻的问题,关于HashMap的更多基础的细节。面试官可能会问出下面的问题:

“你知道HashMap的工作原理吗?” “你知道HashMap的get()方法的工作原理吗?”

你也许会回答“我没有详查标准的Java API,你可以看看Java源代码或者Open JDK。”“我可以用Google找到答案。”

但一些面试者可能可以给出答案,“HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。”这里关键点在于指出,HashMap是在bucket中储存键对象和值对象,作为Map.Entry。这一点有助于理解获取对象的逻辑。如果你没有意识到这一点,或者错误的认为仅仅只在bucket中存储值的话,你将不会回答如何从HashMap中获取对象的逻辑。这个答案相当的正确,也显示出面试者确实知道hashing以及HashMap的工作原理。但是这仅仅是故事的开始,当面试官加入一些Java程序员每天要碰到的实际场景的时候,错误的答案频现。下个问题可能是关于HashMap中的碰撞探测(collision detection)以及碰撞的解决方法:

“当两个对象的hashcode相同会发生什么?” 从这里开始,真正的困惑开始了,一些面试者会回答因为hashcode相同,所以两个对象是相等的,HashMap将会抛出异常,或者不会存储它们。然后面试官可能会提醒他们有equals()和hashCode()两个方法,并告诉他们两个对象就算hashcode相同,但是它们可能并不相等。一些面试者可能就此放弃,而另外一些还能继续挺进,他们回答“因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。”这个答案非常的合理,虽然有很多种处理碰撞的方法,这种方法是最简单的,也正是HashMap的处理方法。但故事还没有完结,面试官会继续问:

“如果两个键的hashcode相同,你如何获取值对象?” 面试者会回答:当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后获取值对象。面试官提醒他如果有两个值对象储存在同一个bucket,他给出答案:将会遍历链表直到找到值对象。面试官会问因为你并没有值对象去比较,你是如何确定确定找到值对象的?除非面试者直到HashMap在链表中存储的是键值对,否则他们不可能回答出这一题。

其中一些记得这个重要知识点的面试者会说,找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。完美的答案!

许多情况下,面试者会在这个环节中出错,因为他们混淆了hashCode()和equals()方法。因为在此之前hashCode()屡屡出现,而equals()方法仅仅在获取值对象的时候才出现。一些优秀的开发者会指出使用不可变的、声明作final的对象,并且采用合适的equals()和hashCode()方法的话,将会减少碰撞的发生,提高效率。不可变性使得能够缓存不同键的hashcode,这将提高整个获取对象的速度,使用String,Interger这样的wrapper类作为键是非常好的选择。

如果你认为到这里已经完结了,那么听到下面这个问题的时候,你会大吃一惊。“如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?”除非你真正知道HashMap的工作原理,否则你将回答不出这道题。默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。

如果你能够回答这道问题,下面的问题来了:“你了解重新调整HashMap大小存在什么问题吗?”你可能回答不上来,这时面试官会提醒你当多线程的情况下,可能产生条件竞争(race condition)。

当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。这个时候,你可以质问面试官,为什么这么奇怪,要在多线程的环境下使用HashMap呢?:)

热心的读者贡献了更多的关于HashMap的问题:

    1. 为什么String, Interger这样的wrapper类适合作为键? String, Interger这样的wrapper类作为HashMap的键是再适合不过了,而且String最为常用。因为String是不可变的,也是final的,而且已经重写了equals()和hashCode()方法了。其他的wrapper类也有这个特点。不可变性是必要的,因为为了要计算hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的hashcode的话,那么就不能从HashMap中找到你想要的对象。不可变性还有其他的优点如线程安全。如果你可以仅仅通过将某个field声明成final就能保证hashCode是不变的,那么请这么做吧。因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的。如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这样就能提高HashMap的性能。

    2. 我们可以使用自定义的对象作为键吗? 这是前一个问题的延伸。当然你可能使用任何对象作为键,只要它遵守了equals()和hashCode()方法的定义规则,并且当对象插入到Map中之后将不会再改变了。如果这个自定义对象时不可变的,那么它已经满足了作为键的条件,因为当它创建之后就已经不能改变了。

    3. 我们可以使用CocurrentHashMap来代替Hashtable吗?这是另外一个很热门的面试题,因为ConcurrentHashMap越来越多人用了。我们知道Hashtable是synchronized的,但是ConcurrentHashMap同步性能更好,因为它仅仅根据同步级别对map的一部分进行上锁。ConcurrentHashMap当然可以代替HashTable,但是HashTable提供更强的线程安全性。看看这篇博客查看Hashtable和ConcurrentHashMap的区别。

IO流

image-20200520104215113

文件操作

File file =new File("C:\\Users\\A450\\Desktop\\bbb");
 //file.createNewFile();//创建一个文件,File写具体目录到创建的文件名
 file.mkdir();//创建一个目录
 System.out.println(file.exists());//判断是否存在
 System.out.println(file.isFile());//判断是否为一个文件
 System.out.println(file.isDirectory());//判断是否为一个目录
 System.out.println(Arrays.toString(file.list()));//获取目录里的文件名以数组方式打印
// file.delete();//删除文件
 file.renameTo(new File("C:\\Users\\A450\\Desktop\\ccc"));//改名字

 

字节输入流

//字节输入流FileInputStream
public static void main(String[] args) throws IOException {
    File file = new File("C:\\Users\\A450\\Desktop\\aaa\\1.txt");
    FileInputStream fis=null;
    try {
       fis= new FileInputStream(file);//
        System.out.println(fis.available());//读取文件长度
        System.out.println(fis.read());//读取文件并返回位置如果读取完返回-1
        byte[] b=new byte[1024];//准备一个byte数组放入读取文件
        int len = fis.read(b);//读取内容放入数组
        while(len!=-1){
            String s=new String(b);//将byte转换字符串
            System.out.println(s);//输出读到的字符串
            len = fis.read(b);
        }

    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        fis.close();
        
    }

}

FileOutputStream

//字符输出流FileOutputStream
public static void main(String[] args) {
    String s="你好,世界!";
    FileOutputStream fos=null;
    try {
        //写入文件,name:文件路径append:默认写入是覆盖写入;有true为追加写入
        fos=new FileOutputStream("C:\\Users\\A450\\Desktop\\aaa\\2.txt",true);
        byte[] b=s.getBytes();//将写入的文件转换为字节数组
        fos.write(b,0,b.length);//写入文件,从0开始,到写入的长度
        fos.flush();//清空缓冲区,强制写入缓冲区文件
    } catch (Exception e) {
        e.printStackTrace();
    }finally{
        fos.close();
    }
}

FileReader

//FileReader字符输入流
public static void main(String[] args) throws IOException {
    File file=new File("C:\\Users\\A450\\Desktop\\aaa\\1.txt");
    FileReader fr=null;
    try {
         fr=new FileReader(file);
         char[] c=new char[1024];//用字符保存读入的数据
        int len = fr.read();
        while (len!=-1){
            String s=new String(c);
            System.out.println(s);
            len=fr.read();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }finally{
        fr.close();
    }
}

FileWriter

     //FileWriter字节输出流
    public static void main(String[] args) throws IOException {
        String s="你好!世界ssss";
        FileWriter fw=null;
        try {
            fw=new FileWriter("C:\\Users\\A450\\Desktop\\aaa\\2.txt",true);
            fw.write(s);
            fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            fw.close();
        
    }
}

BufferReader&BufferWriter

缓冲流

//缓冲流BufferedReader BufferedWriter
public static void main(String[] args) throws IOException {
    File file=new File("C:\\Users\\A450\\Desktop\\aaa\\1.txt");
    BufferedReader br=null;
    BufferedWriter bw=null;
    try {
        br= new BufferedReader(new FileReader(file));
        bw=new BufferedWriter(new FileWriter("C:\\Users\\A450\\Desktop\\aaa\\2.txt"));
        String s=br.readLine();//逐行读取
        while (s!=null){
            System.out.println(s);
            bw.write(s+"\r\n");
            bw.flush();
           s= br.readLine();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }finally{
        br.close();
        bw.close();
    }
}

FileWriter

//打印流
public static void main(String[] args) {
    PrintWriter pw=null;
    try {
        pw=new PrintWriter(new FileWriter("C:\\Users\\A450\\Desktop\\aaa\\2.txt"),true);
        pw.print("hello world");
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        pw.close();
        
    }

}

ObjectOutputStream

//对象输入流 调用的学生类必须引用接口Serializable
public static void main(String[] args) throws IOException {
   Student stu1=new Student("赵东",11);
   Student stu2=new Student("赵2",12);
   Student stu3=new Student("赵3",13);
   Student stu4=new Student("赵4",14);
   ArrayList<Student> al=new ArrayList<Student>();
   al.add(stu1);
   al.add(stu2);
   al.add(stu3);
   al.add(stu4);
   ObjectOutputStream oos=null;
   try {
       oos=new ObjectOutputStream(new FileOutputStream("C:\\Users\\A450\\Desktop\\aaa\\3.txt"));
       oos.writeObject(al);
       oos.flush();
  } catch (IOException e) {
       e.printStackTrace();
  }finally {
       oos.close();

  }
}

ObjectInputStream

//对象输入流ObjectInputStream
public static void main(String[] args) {
   ObjectInputStream ois=null;
   try {
       ois=new ObjectInputStream(new FileInputStream("C:\\Users\\A450\\Desktop\\aaa\\3.txt"));
       ArrayList<Student> al=(ArrayList<Student>)ois.readObject();
       for (Student student : al) {
           System.out.println("姓名"+student.name+"年龄"+student.age);
      }
  } catch (Exception e) {
       e.printStackTrace();
  }
}
posted @ 2021-09-13 22:09  meteor119  阅读(404)  评论(0编辑  收藏  举报