JAVA的一些知识要点
关于集合和泛型
集合常用类
Collection 单列集合根接口
- List 有序的,可以重复
- ArrayList 底层代码使用了Object数组实现,特点在查询速度快,增删慢
- LinkedList 底层代码用的是链表数据结构实现,特点在查询速度慢,增删快
- Vector 底层跟ArrayList一样用的Object数组实现,但是线程是安全的,操作效率较低
- Set 无序的,不可以重复
- HashSet 底层代码用的是哈希表实现,特点在存取速度快
- TreeSet 底层代码用的是二叉树(红黑树) ,特点在会对集合中的元素自动进行排序
- List 有序的,可以重复
Map
- HashMap
- TreeMap
集合的遍历方式
- List遍历,list.add数据后,for循环遍历调用list.get来获取数据或者用高级for
|
|
- iterator迭代器遍历,set.add或者list.add数据后,获取迭代器,迭代器始终为true,使用while,hasNext遍历数据,用X.next获取当前数据
|
|
|
|
foreach循环遍历(高级for),左临时变量获取右集合变量内数据,然后输出临时变量。“底层也是迭代器”,迭代器迭代过程不允许集合对象修改元素的个数,只能用迭代器的方法
1234567ArrayList<String> arrlist = new ArrayList<String>();arrlist.add("This is ArrayList 1");arrlist.add("This is ArrayList 2");arrlist.add("This is ArrayList 3");for(String templist : arrlist){System.out.println(templist);}
map键值遍历,map.put数据后,使用Entry遍历,map对象获取到entrySet,返回Set集合,然后使用foreach循环输出键值对
12345678HashMap<String,String> map = new HashMap<String,String>();map.put("1","Csharp");map.put("2","node");map.put("3","python");Set<Entry<String,String>> entrys = map.entrySet();for(Entry<String,String> entry : entrys){System.out.println("key:"+ entry.getKey() + " value:" + entry.getValue());}
泛型
规范一个对象的类型,把运行时出现的问题提前到编译时(原因是在创建对象或者数组的时候,如果没有指定泛型 ,那么传入多个不同类型的参数时没办法跟返回参数一致。例如一个集合放入字符串String类型,然后获取该集合中的字符串,此时是没问题的,但是如果在该集合中插入添加了Integer类型的情况,那么获取集合中元素或者操作集合元素的时候将会出错,而使用泛型的情况,在编译时就将会提醒用户该集合中插入了不同类型的元素的错误。)
例子
|
|
泛型的好处和注意事项:
- 避免无谓的强制转换类型
- 泛型没有多态的说法或者概念
- 泛型推荐两边都要一致,写单边可以通过是为了兼容老版本系统
|
|
自定义泛型 (自定义泛型的声明可以是合法的名称即可)
自定义泛型就是相当于调用方法传的值 T 是什么类型,而返回就是什么类型。
修饰符 <声明自定义泛型> 返回值类型泛型 函数名(形参)
泛型类的声明
可以在类名上声明出一个自定义泛型,要注意的是:
- 在类上自定义泛型的时候,具体的数据类型是在创建对象的时候指定。
- 在类上自定义的泛型,如果创建该类的对象没有指定泛型的具体类型,那么默认为泛型类中声明的Object类型。
|
|
泛型接口的声明
可以在接口上声明出一个自定义泛型,要注意的是:
- 在接口上自定义泛型的是hi,具体的数据类型是在实现xxxxx接口的时候指定
- 在接口上自定义的泛型,如果实现该接口时没有指定泛型的具体类型,那么默认为Object类型。
- 如果希望在使用创建接口实现类对象的时候再指定接口自定义泛型具体类型,则需要在实现接口出声明出
然后去创建实现类的时候再指定具体类型
|
|
常用IO操作和Thread操作
IO操作的常用类
- 字节流
- InputStream //IO操作中字节流的基类(父类),也是个抽象类
- FileInputStream //向文件写入字节数据的写入字节流
- BufferedInputStream //缓冲写入字节数据的缓冲写入字节流,为写入文件字节提高效率的类
- OutputStream
- FileOutputStream //向文件输出字节数据的输出字节流
- BufferedOutputStream //缓冲输出字节数据的缓冲输出字节流,为输出文件字节提高效率的类
- InputStream //IO操作中字节流的基类(父类),也是个抽象类
- 字符流
- Reader //IO操作中字符流的基类(父类),也是个抽象类
- FileReader //向文件写入字符数据的写入字符流
- BufferedReader //缓冲写入字符数据的缓冲写入字符流,为写入文件字符提高效率的类
- Writer
- FileWriter //向文件输出字符数据的输出字符流
- BufferedWriter //缓冲输出字符数据的缓冲输出字符流,为输出文件字符提高效率的类
- Reader //IO操作中字符流的基类(父类),也是个抽象类
- 转换流(可以把对应的字节流转换为字符流并且还能指定字符集)
- InputStreamReader //输入字节流的转换流
- OutputStreamWriter //输出字节流的转换流
转换流的实际用例
字节输入流转换为字符输入流
12345678910class xxxxx{public static void main(String[] args){InputStream in = System.in;InputStreamReader isr = new InputStreamReader(in); //把控制台输入的字节流转换为字符流BufferedReader br = new BufferedReader(isr);//此时BufferedReader缓冲字符流需要传入的是一个Reader字符流对象,InputStream无法传入,所以需要进行转换String s = br.readLine();System.out.println(s);}}字节输出流转换为字符输出流
123456789101112131415161718192021222324class xxxxx{public static void main(String[] args){Socket socket = new Socket(InetAddress.getLocalHost(),8080);/OutputStream out = socket.getOutputStream();///获取到socket的输出流对象OutputStreamWriter outsw = new OutputStreamWriter(out);//把获取到的输出字节流转换为输出字符流,同时在OutputStreamWriter的构造方法中还有一个形参可以指定字符集,如:new OutputStreamWriter(out,"utf-8")outsw.write("Java IOStream");}public void fileIO(){InputStream in = System.in;InputStreamReader isr = new InputStreamReader(in);BufferedReader br = new BufferedReader(isr);String s = br.readLine();System.out.println(s);FileOutputStream file = new FileOutputStream("C:"+File.separator+"Users"+File.separator+"bitam"+File.separator+"Desktop"+File.separator+"x.txt");//创建文件和程序的数据连接OutputStreamWriter outsw = new OutputStreamWriter(file,"utf-8");//创建一个输出字节转换字符流并指定字符集来写入数据outsw.write(s);//写入操作outsw.close();//关闭IO资源}}
多线程
多线程的创建方式
extends Thread (继承Thread多线程类)
- 创建一个类去继承Thread类
- 覆写Thread的run方法,把自定义线程的任务写在run方法中
- 创建一个Thread的子类对象(该继承了Thread并覆写了run方法的那个类),并且使用start方法来启动该线程
123456789101112131415public class xxxxx extends Thread {public void run(){for(int i =0 ; i<100 ; i++){System.out.println(Thread.currentThread().getName()+":"+i);//获取当前方法线程名并输出i}}public static void main(String[] args){xxxxx x = new xxxxx();x.start();for(int i =0 ; i<100 ; i++){System.out.println(Thread.currentThread().getName()+":"+i);//获取当前方法线程名并输出i}}}implements Runnable (实现Runnable多线程接口)
- 创建一个类实现Runnable接口
- 覆写Runnable的run方法,把自定义线程的任务写在run方法中
- 创建一个Runnable实现类对象(也就是实现了Runnable接口并覆写了run方法的那个类)
- 创建一个Thread对象,然后把Runnable实现类的对象作为参数去传递给Thread对象
- 调用Thread对象的strat方法开启线程
123456789101112131415161718class xxxxx implements Runnable{public void run() {for(int i = 0 ; i<100 ; i++){System.out.println(Thread.currentThread().getName()+":"+i);}}}public class ttttt {public static void main(String[] args){xxxxx x = new xxxxx();Thread t = new Thread(x);t.start();for(int i = 0 ; i<100 ; i++){System.out.println(Thread.currentThread().getName()+":"+i);}}}
线程安全问题的解决方案
线程安全问题出现情况原因:
- 必须有存在两个或者以上的线程共享着一个资源的情况才会出现
- 操作共享资源的代码必须有两句或者以上
线程安全的解决方式
同步代码块
123synchronized(锁){需要被同步的代码}同步函数
123修饰符 synchronized 返回值类型 函数名(形参列表...){}
注意事项:
- 同步代码块的锁可以使任意的对象,同步函数的锁是固定的
- 非静态函数的锁对象是this对象,静态函数的锁对象是class对象
- 锁对象必须是多线程共享的对象,否则锁不住
- 在同步代码块或者是同步函数中调用sleep方法是不会释放锁对象的,如果是调用了wait方法是会释放锁对象的
关于==和equals的区别
从简单来说
==是字面意思,等于。equals则是比较,是否相同。而等于跟相同是两个概念,可以从以下例子来理解:
|
|
从Java角度来说
==是判断两个变量或者对象实例是不是指向相同的内存栈(地址值),equals则判断两个变量或者对象实例的hashcode是不是相同(相同引用),使用==的情况会对内存地址值进行比较,equals则对该对象的hashcode进行比较。
|
|
实战中
如果只比较两个基本数据类型值,那么只需要使用==,而不能使用equals,因为equals是个函数(方法),而基本类型如int,float,double等不是对象,那么就没有函数,所以是无法equals的。
|
|
所以如果比较的是值,建议使用==,而不用equals,具体原因如下。
equals底层源码是:
|
|
传入一个对象,如果对比两个实例是否是同一个实例(instanceof),如果是则继续判断该实例对象的hashcode跟该被比较对象的hashcode用==对比,相同则返回true,否则返回false。
而如果传入对象后对比两个实例根本不是同一个实例,那么也就不用在进一步用==对比地址值,直接返回false。
也就是equals其实也是直接调用了==操作,不过是当实例相同时调用它来对比hashcode。
如果想对比一个对象的地址值是否相同,应该按照自己的需求去覆写equals方法。而且如果在不覆写equals方法的情况,使用==就是比较地址值,使用equals就是比较是否同一引用和hashcode是否相同。
Junit 常用注释:
测试体@Test
//测试体不能是static修饰和不能有形参
非静态测试前环境@Before
非静态测试后清理@After
//不带Class在每个测试方法测试的时候会调用一次
静态测试前环境 @BeforeClass
静态测试后清理@AfterClass
//带Class在所有测试方法测试之前和测试之后的时候只调用一次