笔记-狂神说Java-API,集合, IO
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));
//除法除不尽可以在后面加,保留几位小数 ,属性(四舍五入)
克隆技术
类要实现克隆,需要做两步:
-
实现Cloneable接口;
-
重写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:字符串,使用一对“”引起来表示
-
String声明为final的,不可被继承
-
String实现了Serializable接口:表示字符串是支持序列化的
-
实现了comparable接口:表示String可以比较大小
-
String内部定义了final char【】 value用于存储字符串数据
-
String:代表不可变的字符序列:简称:不可变性。
体现:当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
-
通过字面量的方式给一个字符串赋值,此时的字符串值声明在字符串常量池中,字符串常量池中是不会存储相同内容的字符串。
基本类型与字符串之间的转换
基本类型--》字符串
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();集合
-
重点:HashMap
①HashMap的工作原理
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
②HashMap和Hashtable的区别
HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。
-
HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
-
HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
-
另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
-
由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
-
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的区别
HashMap | HashSet |
---|---|
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的问题:
-
为什么String, Interger这样的wrapper类适合作为键? String, Interger这样的wrapper类作为HashMap的键是再适合不过了,而且String最为常用。因为String是不可变的,也是final的,而且已经重写了equals()和hashCode()方法了。其他的wrapper类也有这个特点。不可变性是必要的,因为为了要计算hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的hashcode的话,那么就不能从HashMap中找到你想要的对象。不可变性还有其他的优点如线程安全。如果你可以仅仅通过将某个field声明成final就能保证hashCode是不变的,那么请这么做吧。因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的。如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这样就能提高HashMap的性能。
-
我们可以使用自定义的对象作为键吗? 这是前一个问题的延伸。当然你可能使用任何对象作为键,只要它遵守了equals()和hashCode()方法的定义规则,并且当对象插入到Map中之后将不会再改变了。如果这个自定义对象时不可变的,那么它已经满足了作为键的条件,因为当它创建之后就已经不能改变了。
-
我们可以使用CocurrentHashMap来代替Hashtable吗?这是另外一个很热门的面试题,因为ConcurrentHashMap越来越多人用了。我们知道Hashtable是synchronized的,但是ConcurrentHashMap同步性能更好,因为它仅仅根据同步级别对map的一部分进行上锁。ConcurrentHashMap当然可以代替HashTable,但是HashTable提供更强的线程安全性。看看这篇博客查看Hashtable和ConcurrentHashMap的区别。
-
IO流
文件操作
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();
}
}