JavaSE日常笔记汇总
1. If和switch的比较
2. continue的注意事项
在for循环中,当执行continue语句时,i++还是会执行,continue语句只代表此次循环结束,i还是会累加,并继续执行下次循环。
3. 内部类
- 内部类的访问规则
- 内部类可以直接访问外部类中的成员,包括私有。之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用。
引用格式: 外部类名.this
- 外部类访问内部类必须建立内部类对象。
- 内部类的访问格式
- 当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,直接建立内部类对象。
格式:外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
例: Outer.Inner in = new Outer().new Inner();
- 当内部类在成员位置上时就可以被成员修饰符修饰。
比如:private 将内部类在外部类中进行封装。
static 内部类就具备了static的特性。
当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限。
- 在外部其他类中,如何直接访问static内部类的非静态成员呢?
new 外部类名.内部类名().方法名();
例: new Outer.Inner().function();
因为内部类为static的,所以不必建立外部类对象,只要调用外部类类名就可,如何建立内部类对象,通过.即可访问方法了。
- 在外部其他类中,如何直接访问static内部类的静态成员呢?
外部类名.内部类名().方法名();
例: Outer.Inner().function();
因为内部类与方法都是static的,所以不必建立对象,可以通过类名直接访问。
- 注意:
当内部类中定义了静态成员,该内部类必须是static的。
当外部类中的静态方法访问内部类时,内部类也必须是static的。
- 当描述事物时,该事物的内部还有事物,那事物用内部类来描述。
因为内部事物在使用外部事物的内容。
内部类定义在局部时需要注意2点: 比如定义在一个方法内
一是不可以被成员修饰符修饰。
二是可以直接访问外部类中的成员,因为还持有外部类中的引用,但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量。
- 匿名内部类
- 匿名内部类其实就是内部类的简写格式。
- 定义匿名内部类的前提:内部类必须是继承一个类或者实现一个接口。
- 匿名内部类的格式: new 父类或者接口( ) {定义子类的内容}
- 其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。可以理解为带内容的对象。
- 匿名内部类中定义的方法最好不要超过3个。
4. for循环的注意事项
5. String和StringBuilder类中的常用方法
请注意:有StringBuffer和StringBuilder类,以后开发,建议使用StringBuilder类。
StringBuilder的好处: 1、提高效率;2、简化书写;3、提高安全性。
- 1. String类中的构造方法如下:
- String(char[] value) :将字符数组转成字符串,也可以是byte或其他数组。
- String(char[] value, int offset, int count):将字符数组的一部分转成字符串,也可以是byte数组。
- charAt(int index) :获取index索引处的值,返回char类型
- endsWith(String suffix) :测试此字符串是否以指定的后缀结尾。
- equals(Object anObject) :将此字符串与指定对象进行比较(比较大小写)。
- equalsIgnoreCase(String anotherString) :将此 String与其他 String比较,忽略大小写。
- hashCode() :返回此字符串的哈希码。
- indexOf(int ch) :返回指定字符第一次出现的字符串内的索引。
- indexOf(int ch, int fromIndex) :以指定的索引开始搜索,返回指定字符第一次出现的字符串内的索引。
- indexOf(String str) :返回指定子字符串第一次出现的字符串内的索引。
- indexOf(String str, int fromIndex) :从指定的索引开始,返回指定子串的第一次出现的字符串中的索引。
- boolean isEmpty(): 判断字符串是不是空串,如果是空的就返回true
- lastIndexOf(int ch) :返回指定字符在此字符串中最后一次出现处的索引。
- int lastIndexOf(String str) :返回指定子字符串在此字符串中最右边出现处的索引。
- length():返回此字符串的长度。
- replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
- split(String regex) :根据给定正则表达式的匹配拆分此字符串。
- startsWith(String prefix):测试此字符串是否以指定的前缀开始。
- substring(int beginIndex):返回一个新的字符串,它是此字符串的一个子字符串。
- substring(int beginIndex, int endIndex):返回一个新字符串,它是此字符串的一个子字符串。(包含头不包含尾)
- toCharArray():将此字符串转换为一个新的字符数组。
- toLowerCase() :使用默认语言环境的规则将此 String 中的所有字符都转换为小写。
- toUpperCase():使用默认语言环境的规则将此 String 中的所有字符都转换为大写。
- trim():返回字符串的副本,忽略前导空白和尾部空白。
- static String valueOf(Object obj):静态方法,返回 Object 参数(可以是整数,字符等)的字符串表示形式。
- String类中的普通方法如下:
- 3. StringBuilder类中的构造方法如下:
- StringBuilder(String str):构造一个字符串生成器,并初始化为指定的字符串内容。
- 4. StringBuilder类中普通方法如下:
- append(Object obj):追加 Object 参数(可以是字符串,整数,字符等)的字符串表示形式。
- charAt(int index):返回此序列中指定索引处的 char 值。
- delete(int start, int end):移除此序列的子字符串中的字符。
- indexOf(String str):返回第一次出现的指定子字符串在该字符串中的索引。
- insert(int offset, Object obj):将 Object 参数(包括字符串,整数等)的字符串表示形式插入此字符序列中。
- length() :返回长度(字符数)。
- replace(int start, int end, String str):使用给定 String 中的字符替换此序列的子字符串中的字符。
- reverse():将此字符序列用其反转形式取代。
- setCharAt(int index, char ch):将给定索引处的字符设置为 ch。
- substring(int start):返回一个新的 String,它包含此字符序列当前所包含字符的子序列。
- substring(int start, int end):返回一个新的 String,它包含此序列当前所包含字符的子序列。
- toString():返回此序列中数据的字符串表示形式。
6. Character和Integer中的常用方法
1. Character中的常用方法:
- isLetter(char ch):确定指定字符是否为字母。
- isDigit(char ch):确定指定字符是否为数字。
- isLetterOrDigit(char ch):确定指定字符是否为字母或数字。
- isLowerCase(char ch):确定指定字符是否为小写字母。
- isUpperCase(char ch):确定指定字符是否为大写字母。
- toString():返回表示此 Character 值的 String 对象。
- toLowerCase(char ch):使用取自 UnicodeData 文件的大小写映射信息将字符参数转换为小写。
- toUpperCase(char ch):使用取自 UnicodeData 文件的大小写映射信息将字符参数转换为大写。
2. Integer中的常用方法:
- parseInt(String s):将字符串参数作为有符号的十进制整数进行解析。即将字符串转成int型整数。
- toBinaryString(int i):以二进制(基数 2)无符号整数形式返回一个整数参数的字符串表示形式。
- toHexString(int i):以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式。
- toOctalString(int i):以八进制(基数 8)无符号整数形式返回一个整数参数的字符串表示形式。
- toString():返回一个表示该 Integer 值的 String 对象。
7. String和StringBuilder的注意事项
String和StringBuilder的底层都是使用字符数组来保存数据,其中的区别如下:
String是不可变的: String的底层字符数组如此所示:private final char value[];
final表示该内置的数组被赋值后不能改变地址值
private 修饰,而且没有提供set方法来修改,意味着一旦创建对象后就不能修改了
StringBuilder是可以被改变的: StringBuilder的底层字符数组如此所示: char[] value;
没有使用final和private修饰,表示StringBuilder可以后期修改。
怎么选取使用哪个?
保存字符数据 ------------> String
反复修改数据(拼接,删除,修改)------------>StringBuilder
8. String和int之间的转换问题
int类型和String类型是能相互转换的,转换方法如下:
int à String :
可以直接拼接字符串,如 5 + “”;
可以通过String类中的一个方法:public static String valueOf(int i)
String à int
可以通过Integer类中的一个方法:public static int parseInt(String s)
9. 装箱拆箱的注意事项和判断对象为null
- 在JDK1.5以后,新增了如下的2个特性,即不需要手动转换,可以把int赋值给Integer
自动装箱:把基本数据类型转换为对应的包装类类型
public static Integer valueOf(int i)
自动拆箱:把包装类类型转换为对应的基本数据类型
public int intValue();
- 在使用包装类类型的新特性时,如果做操作,最好先判断是否为null,以后在开发中,只要是对象,在使用前就必须进行不为null的判断。
10. 多态中的注意事项
11. 多态中用来判断是否能够强转的关键字
12. 接口的成员特点
13. JDK1.8以后接口中可以定义完整方法
JDK1.7版本中,接口里只能定义抽象方法,在JDK1.8版本后接口中可以定义普通方法了
14. 抽象类和接口的区别
15. Date类的相关知识
A. Date类是一个与时间相关的类,现主要有2个构造方法和2个普通方法需掌握:
- Date d = new Date(); 传入的是当前的系统时间,当对d进行打印输出时,会输出如下值:Mon Apr 02 16:58:25 CST 2018
- Date d = new Date(long); 传入的是long类型的毫秒值,当对d进行打印输出时,会输出如下值:Thu Jan 01 08:00:01 CST 1970。在1970标准时间上加上输入的毫秒进行打印输出。
- public void setTime(long time):设置时间,给的是毫秒值。如同构造方法中传入的值一样,当对调用此方法的对象进行打印输出时,是在1970标准时间上加上输入的毫秒进行打印输出。
- public long getTime():获取的是毫秒值。从1970年1月1日 00:00:00开始的。如果没有设置值,当对该返回值进行打印输出时,打印的是系统时间离1970年的毫秒值,如果有设置,打印的是设置的毫秒值。
B. SimpleDateFormat类的概述和使用:SimpleDateFormat是一个以与语言环境有关的方式来格式化和解析日期的具体类。该类中主要有3个功能需掌握,一个是对日期进行格式化(日期 -> 文本),能将上述Date类中的不规则日期格式变成我们想要的格式进行输出;还有一个能将我们输入的文本进行解析(文本 -> 日期),能将我们输入不同格式的文本进行解析,返回一个Date类型的值;第三个是当创建该类对象时没有使用构造函数赋默认格式时可以使用applyPattern方法赋予指定格式。
- public final String format(Date date)方法实现如下:
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String s = sdf.format(d);
System.out.println(s);
使用此方法时,可以在创建该类对象时使用构造函数赋值,把我们需要的格式传入进去,当我们调用该方法时,将Date类的对象传入进去时,就能将格式变更为我们需要的格式,并返回一个字符串类型。
- public Date parse(String source)方法实现如下:
String str = "2080-08-08 12:23:45";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(str);
System.out.println(d);
使用此方法时,可以在创建该类对象时使用构造函数赋值,把我们需要的格式传入进去,当我们调用该方法时,将我们输入的与格式匹配的字符串传入,就会将我们输入的字符解析出来,返回一个Date对象。
- public void applyPattern(String pattern)方法实现如下:
SimpleDateFormat sdf = new SimpleDateFormat();
Sdf. applyPattern("yyyy-MM-dd HH:mm:ss");
当创建该类对象时没有使用构造函数赋予默认格式,或程序后期需更改指定的格式时,可以通过此方法给这个对象赋予需要的时间格式。
16. 工具类的注意事项
1. Arrays类是一个工具类,以此类举例分析工具类。
2. Arrays类中有构造方法吗?我们为什么在帮助文档上没有看到Arrays类的构造方法?
一个类如果没有构造方法,系统将提供一个无参的构造方法。而我们没有在帮助文档上看到Arrays类的构造方法是因为这个类的构造方法被私有化了,外界是无法使用的,所以在帮助文档就看不到,通过源码我们找到了 private Arrays() { }。
3. Arrays类的这种设计是常用的工具类的设计思想:把构造方法私有;成员全部用static修饰。
4. 工具类中的成员属性一般设置为private final static(私有,不可修改,静态)类型。保证不能让外部修改成员属性。
5. 连接池对象应该是一个应用只创建一次就可以的,不需要每次使用都创建一个新的连接池。此时可以直接将连接池设置为静态成员常量,如果要使用可以设置方法返回该常量,这样一个应用就只有一个连接池。
17. 集合概述
集合是一个容器,用来存储和获取数据的,集合主要可以分为2大类:
单列集合(Collection):
单列集合又可以分为List(元素可重复,有序)集合,和Set(不可重复,无序)集合
双列集合(Map):
双列集合的主要实现类为HashMap
18. List集合概述
List集合是一个接口,其中的元素特点是有序和可以重复。该接口继承自Collection
List集合的2个具体实现类为:
ArrayList集合:排序方式为数组排序,查找块,增删慢。
LinkedList集合:排序方式为链表排序,查找慢,增删慢。
List集合中的主要实现方法有:
void add(int index,E element):在指定位置添加元素
E remove(int index):删除指定位置的元素
E get(int index):获取指定位置的元素
E set(int index,E element):修改指定位置的元素
List集合中的三种遍历方式:
迭代器:可以使用Iterator迭代和ListIterator迭代
Iterator迭代器中只有显示元素和移除元素的功能。
ListIterator迭代器中还有增加元素等功能。
增强for:可以使用foreach遍历元素
普通for:因为List集合是有序的,故可以使用普通for遍历元素
并发修改异常:ConcurrentModificationException
当方法检测到对象的并发修改,但不允许这种修+改时,抛出此异常。
产生的原因:迭代器依赖于集合而存在,在判断成功后,集合中添加了新的元素,而迭
代器并不知道,所有就报错了。
解决方法:可以使用ListIterator迭代器,在迭代过程中可以修改元素
可以使用普通for遍历,在遍历过程中可以对元素进行修改
19. Set集合概述
Set集合是一个接口,其中的元素特点是元素唯一,存储元素无序。该接口继承自Collection
Set集合只有一个具体实现类:HashSet集合。
它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变
添加功能的执行过程中,是进行了数据的判断的。分别使用HashCode()和equals()判断
HashCode():对数据的Hash值进行判断,如果Hash不同,就直接将元素添加
Equals():如果Hash值相同,就对数据使用equals()方法进行判断,如果不同就添加
如果我们使用HashSet集合存储对象,你要想保证元素的唯一性,就必须重写hashCode()和equals()方法。
HashSet集合的遍历:
因为HashSet集合是无序的,故只能使用迭代器和增强for进行遍历。
HashSet集合中的成员方法:
因为HashSet实现Set接口,Set又继承自Collection,故一般使用Collection中的方法
20. Map集合概述
Map集合是一个接口,其实现类为HashMap,使用特点如下:
将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
如:学生的学号和姓名(it001 林青霞)(it002 张曼玉)(it003 王祖贤)
HashMap集合中的主要成员方法:
V put(K key,V value):添加元素
V remove(Object key):根据键删除键值对元素
void clear():移除所有的键值对元素
boolean containsKey(Object key):判断集合是否包含指定的键
boolean containsValue(Object value):判断集合是否包含指定的值
boolean isEmpty():判断集合是否为空
int size():返回集合中的键值对的对数
V get(Object key):根据键获取值
Set<K> keySet():获取所有键的集合
Collection<V> values():获取所有值的集合
Set< EntrySet<键 , 值>> entrySet():获取HashMap集合中所有的键值对
HashMap集合的遍历:
使用keySet()方法获取所有键的集合,在通过迭代器或者增强for遍历集合
通过entrySet()方法获取集合中所有的键值对,再通过迭代器或增强for遍历集合
HashMap集合的注意事项:
问题来源:使用该集合时,如果把对象作为键存入,当对象里的属性改变时,键所对应的Hash值也会改变,这时使用get(Object key)方法用来获取该键对应的值时会出错。
应对措施:1.如果将对象作为键使用时,使用put方法添加到集合中后,建议不要再修改该对象中的属性,因为修改后该键的Hash值也会相应改变,会导致不能获取到值。2.在遍历HashMap集合时,建议使用entrySet方法获取所有的键值对,再遍历。
21. IO流之File类概述
File类的概述:
File是文件和目录路径名的抽象表示形式,也就是说文件和目录是可以通过File封装成对象的。
目录:其实就是文件夹
File类的构造方法:
File(String pathname):通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
File(String parent, String child):根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
File(File parent, String child):根据parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
File类中的主要成员方法:
public boolean createNewFile():创建文件;文件不存在,创建文件返回true;文件存在,创建文件失败并返回false
public boolean mkdir():创建目录;如果目录不存在,创建目录并返回true;目录存在,创建目录失败并返回false
public boolean mkdirs():创建多级目录;一般情况下可以使用mkdirs()方法创建单级目录。
public boolean delete():删除文件和目录;
注意:如果一个目录中有内容(目录,文件),就不能直接删除。应该先删除目录中的内容,最后才能删除目录。
public boolean isDirectory():判断是否是目录
public boolean isFile():判断是否是文件
public boolean exists():判断是否存在
public String getAbsolutePath():获取绝对路径
public String getPath():获取相对路径
public String getName():获取名称
public String[] list()返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。
相对路径和绝对路径;
绝对路径:是以盘符开始的路径。d:\\aa\\b.txt
相对路径:不以盘符开始。相对于当前的项目而言,在项目的目录下。如何显示出来呢?刷新项目就可以了。
22. IO流之字节流
字节流的概述:
字节流可以分为字节输入流和字节输出流。
字节输出流写数据:FileOutputStream
OutputStream:这是抽象类,是表示输出字节流的所有类的超类
FileOutputStream:这是字节输出流的具体实现类,将数据写入 File
构造方法:FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(File file, boolean append) :第二个参数是true即可实现数据的追加写入。
字节流写数据的步骤:
A:创建字节输出流对象
B:调用写数据的方法
C:释放资源
字节输出流中用到的主要成员方法:
public void write(int b):一次写一个字节
public void write(byte[] b):一次写一个字节数组
public void write(byte[] b,int off,int len):一次写一个字节数组的一部分
String类中一个方法:byte[] getBytes() 将字符串转换为字节数组
字节输入流读数据:FileInputStream
InputStream:这是抽象类,是表示输入字节流的所有类的超类
FileInputStream:这是字节输入流的具体实现类,将数据写入 File
构造方法:FileInputStream (String name):创建一个向具有指定名称的文件中写入数据的输入文件流。
字节流读数据的步骤:
A:创建字节输入流对象
B:调用读数据的方法
C:释放资源
字节输入流中用到的主要成员方法:
public int read(byte[] b):从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中
返回值是读入缓冲区的字节总数,也就是实际的读取个数如果因为已经到达文件末尾而没有更多的数据,则返回 -1。
read():从此输入流中读取单个字节。
当文件小于1G时,可以尝试如下方式:
FileInputStream fis = new FileInputStream(""); //创建读取流对象
FileOutputStream fos = new FileOutputStream(""); //创建写入流对象
//定义一个字节数组,可以装下整个读取的文件
byte[] arr = new byte[fis.available()]; // 文件有多大, 我的数据就有多大
fis.read(arr); //将文件读入arr字节数组中
fos.write(arr); //将arr字节数组中的内容写入到指定的位置
23. IO流之字节缓冲流
字节缓冲流概述:
字节流一次读写一个数组的速度比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想,所以提供了字节缓冲区流。字节缓冲区流仅仅提供缓冲区,而真正的底层的读写数据还得需要基本的流对象进行操作。
字节缓冲输出流:BufferedOutputStream
需在创建该对象时传入FileOutputStream(字节输出流)对象,方法可以使用字节输出流中的方法。
字节缓冲输入流:BufferedInputStream
需在创建该对象时传入FileInputStream (字节输入流)对象,方法可以使用字节输入流中的方法。
24. IO流之转换流(了解)
转化流概述:转换流 = 字节流 + 编码表
字节流一次读取一个字节的方式读取带有汉字的文件是有问题的,因为你读取到一个字节后就转为字符在控制台输出了,而汉字是由2个字节组成的,所以这里会出问题。文件复制的时候,字节流读取一个字节,写入一个字节,这个没有出现问题,是因为最终底层会根据字节做拼接,得到汉字。
汉字存储的规则:左边的字节数据肯定是负数,右边的字节数据可能是负数,也可能是正数,一般是负数。
编码表了解:
ASCII : 美国标准信息交换码, 用一个字节的7位表示数据
ISO-8859-1 : 欧洲码表, 用一个字节的8位表示数据, 兼容ASCII
GB2312 : 中文码表的升级版, 融合了更多的中文文字符号, 兼容ASCII
UTF-8 : 是一种可变长度的字符编码, 用1-3个字节表示数据, 又称为万国码, 兼容ASCII,用在网页上可以统一页
面中的中文简体繁体和其他语言的显示.
如果出现乱码即为编码和解码时使用的码表不一样。在写代码时得保证编码和解码时码表一致。
编码:把看得懂的变成看不懂的。 解码:把看不懂的变成看得懂的。
转换流的构造方法:
OutputStreamWriter 字符输出流
public OutputStreamWriter(OutputStream out):根据默认编码把字节流的数据转换为字符流
public OutputStreamWriter(OutputStream out,String charsetName):根据指定编码把字节流数据转换为字符流
InputStreamReader 字符输入流
public InputStreamReader(InputStream in):用默认的编码读数据
public InputStreamReader(InputStream in,String charsetName):用指定的编码读取数据
OutputStreamWriter(字符输出流)写数据方法:
public void write(int c):写一个字符
public void write(char[] cbuf):写一个字符数组
public void write(char[] cbuf,int off,int len):写一个字符数组的一部分
public void write(String str):写一个字符串
public void write(String str,int off,int len):写一个字符串的一部分
InputStreamReader(字符输入流)读数据方法:
public int read():一次读取一个字符
public int read(char[] cbuf):一次读取一个字符数组
25. IO流之字符流
字符流概述:
转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流提供了对应的子类。一般情况是不要用转换流的。
字符流的构造方法:
FileWriter 字符输出流,写文件
FileWriter():需传入一个File对象,或者一个字符串的文件地址。
FileWriter(File file, boolean append):第二个参数是true即可实现数据的追加写入。
FileReader 字符输入流,读文件
FileReader():需传入一个File对象,或者一个字符串的文件地址。
FileWriter(字符输出流)写数据方法:
public void write(int c):写一个字符
public void write(char[] cbuf):写一个字符数组
public void write(char[] cbuf,int off,int len):写一个字符数组的一部分
public void write(String str):写一个字符串
public void write(String str,int off,int len):写一个字符串的一部分
FileReader(字符输入流)读数据方法:
public int read():一次读取一个字符
public int read(char[] cbuf):一次读取一个字符数组
26. IO流之字符缓冲流
BufferedWriter:字符缓冲输出流
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。可以指定缓冲区的大小,
或者接受默认的大小。在大多数情况下,默认值就足够大了。
构造方法:BufferedWriter(Writer out):需传入一个字符输出流。
普通方法:字符缓冲输出流的普通方法和字符输出流中的普通方法一样运用。
特有方法:void newLine():写入一个行分隔符,这个行分隔符是由系统决定的。当使用这个方法后请刷新flush()。
BufferedReader:字符缓冲输入流
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 可以指定缓冲区的大小,或者
可使用默认的大小。大多数情况下,默认值就足够大了。
构造方法:BufferedReader(Reader in):需传入一个字符输入流。
普通方法:字符缓冲输入流的普通方法和字符输入流中的普通方法一样运用。
特有方法:String readLine():包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null。
27. JDK1.7后IO流的新特性
try(FileReader fr = new FileReader("src/1.txt"); FileWriter fw = new FileWriter("src/2.txt") ){ } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } |
当把IO流的创建对象的过程写在try内部时,不用进行关流操作,会自动关流。
28. IO流之汇总
- 1. 在IO流中,有8种流必须掌握:
字节流------->万能流(但是不适合 读取文本)
- 字节输出流
FileOutputStream
BufferedOutputStream : FileOutputStream 的升级版
- 字节输入流
FileInputStream
BufferedInputStream : FileInputStream的升级版
字符流-----> 只适合读取文本
- 字符输出流
FileWrite
BufferedWrite : FileWrite的升级版
- 字符输入流
FileRead
BufferedRead : FileRead的升级版
- 2. 字符缓冲流的特殊功能:
跨系统换行符 ------> newLine()
一次读取一行数据---> readLine()
BufferedReader br = new BufferedReader(new FileReader("窗里窗外.txt"));
String line;
while((line=br.readLine())!=null) {//这里的返回值是一个 null 不是 -1, 因为该方法的返回值是String,
System.out.println(line);
}
br.close();
29. MySQL(单表)知识汇总
- 1. SQL的分类
- DDL(Data Definition Language):数据定义语言,用来定义数据库对象:库、表、列等;create,drop,alter..
- DCL(Data Control Language):数据控制语言,用来定义访问权限和安全级别;grant,if…
- DML(Data Manipulation Language):数据操作语言,用来定义数据库记录(数据);insert,update,delete…
- DQL(Data Query Language):数据查询语言,用来查询记录(数据);
- 2. SQL对数据库进行操作
- 创建数据库:
create database 数据库名称 [character set 字符集 collate 字符集校对规则];
- 查看数据库:
查看数据库服务器中所有的数据库:show databases;
查看某个数据库的定义信息: show create database 数据库名称;
- 修改数据库:
alter database 数据库名称 character set 字符集 collate 校对规则;
- 删除数据库:
drop database 数据库名称;
- 其他数据库操作:
切换数据库:use 数据库名称;
查看当前正在使用的数据库:select database();
- 3. SQL对数据库表进行操作
- SQL创建表:
create table 表名称(字段名称 字段类型(长度) 约束,字段名称 字段类型(长度) 约束…);
- SQL查看表:
查看某个数据库下的所有的表: show tables;
查看某个表的结构信息: desc 表名;
- SQL删除表:
drop table 表名;
- SQL修改表:
添加列: alter table 表名 add 列名 类型(长度) 约束;
修改列类型,长度和约束:alter table 表名 modify 列名 类型(长度) 约束;
删除列:alter table 表名 drop 列名;
修改列名称:alter table 表名 change 旧列名 新列名 类型(长度) 约束;
修改表名:rename table 表名 to 新的表名;
修改表的字符集:alter table 表名 character set 字符集;
- 4. SQL对数据库表的记录进行操作
- SQL添加表的记录:
向表中插入某些列:insert into 表名 (列名1,列名2,列名3…) values (值1,值2,值3…);
向表中插入所有列:insert into 表名 values (值1,值2,值3…);
- SQL修改表的记录:
update 表名 set 列名=值,列名=值 [where 条件];
- SQL删除表的记录
delete from 表名 [where 条件];
- SQL查询表的记录:
重点,请查看下述MySQL查询笔记。
30. MySQL(多表)知识汇总
- 1. MySQL中的约束(constraint)
- a. MySQL中约束是用来保证数据的完整性。可以分为主键约束(primary key),唯一约束(unique),非空约束(not null),自动增长列(auto_increment),外键约束(foreign key)。
- b. 外键约束:
外键约束的添加方式:constraint fk_score_sid(约束名) foreign key (sid) references student(id);
ALTER TABLE score1 ADD CONSTRAINT fk_stu_score FOREIGN KEY(sid) REFERENCES stu(id);
也可以直接在SQLyog中直接右击表,点击关联/外键按钮创建外键。
- c. 外键约束的说明:
有2个表score和student,当在score中添加外键约束时,是以student为主表进行统计,此时student表中必须有主键约束,在score中添加student的主键为score的外键约束。
- 2. MySQL中的另一知识点(事务):
a) 事务的概念、实现方法和基本特性:
事务:指的是逻辑上的一组操作,组成这组操作的各个逻辑单元,要么全都成功,要么全都失败。
开启事务:start transaction; |
提交事务:commit; |
回滚事务:rollback; |
原子性:事务的不可分割,组成事务的各个逻辑单元不可分割。
一致性:事务执行的前后,数据完整性保持一致。
隔离性:事务执行不应该受到其他事务的干扰。
持久性:事务一旦结束,数据就持久化到数据库中。
b) 事务的隔离级别:
隔离性:一个事务的执行,不应该受到其他事务的干扰。如果不考虑隔离性(一个事务执行受到其他的事务的干扰),引发一些安全问题。 |
|
脏读:一个事务读到了另一个事务未提交的数据,导致查询结果不一致。 |
|
不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致多次查询结果不一致。 |
|
虚读/幻读:一个事务读到了另一个事务已经提交的insert的数据,导致多次查询结果不一致。 |
|
read uncommitted: |
脏读,不可重复读,虚读都有可能发生 |
read committed: |
避免脏读。但是不可重复读和虚读是有可能发生 |
repeatable read: |
避免脏读和不可重复读,但是虚读有可能发生。 |
serializable: |
避免脏读,不可重复读,虚读。 |
31. MySQL(查询)
1. 单表查询
a. 基础查询:
SELECT * FROM stu;
SELECT sid, sname, age FROM stu;
b. 条件查询:在where语句中使用如下关键字=、!=、<>、<、<=、>、>=;BETWEEN…AND; IN(set);IS NULL; IS NOT NULL;AND;OR;NOT;
查询姓名不为null的学生记录:SELECT * FROM stu WHERE sname IS NOT NULL;
查询性别非男的学生记录:SELECT * FROM stu WHERE gender!='male';
查询学号不是S_1001,S_1002,S_1003的记录:
SELECT * FROM tab_student WHERE s_number NOT IN ('S_1001','S_1002','S_1003');
c. 模糊查询:在where语句中使用关键字like,并使用通配符 _(一个字符)或者%(0-n个字符)。(下划线或百分号)
查询姓名由5个字母构成的学生记录:SELECT * FROM stu WHERE sname LIKE '_____';
查询姓名中第2个字母为“i”的学生记录:SELECT * FROM stu WHERE sname LIKE '_i%';
d. 字段控制查询:
- 去除重复记录:例如emp表中sal字段就存在相同的记录。当只查询emp表的sal字段时,那么会出现重复记录,那么想去除重复记录,需要使用DISTINCT: SELECT DISTINCT sal FROM emp;
- 求和:因为sal和comm两列的类型都是数值类型,所以可以做加运算。如果sal或comm中有一个字段不是数值类型,那么会出错。 SELECT *,sal+comm FROM emp;
- 去空函数:comm列有很多记录的值为NULL,因为任何东西与NULL相加结果还是NULL,所以结果可能会出现NULL。下面使用了把NULL转换成数值0的函数IFNULL:SELECT *,sal+IFNULL(comm,0) FROM emp;
- 添加别名:在上面查询中出现列名为sal+IFNULL(comm,0),这很不美观,现在我们给这一列给出一个别名,为total: SELECT *, sal+IFNULL(comm,0) AS total FROM emp; 其中的as关键字可以省略。
e. 排序: order by 列名 asc(默认) desc
查询所有学生记录,按年龄升序排序:SELECT * FROM stu ORDER BY sage ASC;
查询所有学生记录,按年龄降序排序:SELECT * FROM stu ORDER BY age DESC;
查询所有雇员,按月薪降序排序,如果月薪相同时,按编号升序排序:
SELECT * FROM emp ORDER BY sal DESC,empno ASC;
f. 聚合函数 sum avg max min count
查询emp表中记录数:SELECT COUNT(*) AS cnt FROM emp;
查询emp表中有佣金的人数:SELECT COUNT(comm) cnt FROM emp; count只统计不为null的列数。
查询所有雇员月薪和:SELECT SUM(sal) FROM emp;
查询所有雇员月薪+佣金和:SELECT SUM(sal+IFNULL(comm,0)) FROM emp;
统计所有员工平均工资:SELECT AVG(sal) FROM emp;
查询最高工资和最低工资:SELECT MAX(sal), MIN(sal) FROM emp;
g. 分组查询 需使用GROUP BY子句,注:凡和聚合函数同时出现的列名,一定要写在group by 之后
查询每个部门的部门编号和每个部门的工资和:SELECT deptno, SUM(sal) FROM emp GROUP BY deptno;
查询每个部门的部门编号以及每个部门的人数:SELECT deptno,COUNT(*) FROM emp GROUP BY deptno;
查询工资总和大于9000的部门编号以及工资和:
SELECT deptno, SUM(sal) FROM emp GROUP BY deptno HAVING SUM(sal) > 9000;
h. having与where的区别:
having是在分组后对数据进行过滤,where是在分组前对数据进行过滤。
having后面可以使用聚合函数(统计函数), where后面不可以使用聚合函数。
WHERE是对分组前数据的约束;而HAVING是对分组后数据的约束。
i. 查询中各关键词的使用顺序:
from -> where -> group by -> having -> select -> order by
2. 多表查询
- a. 合并结果集:union、 union all
作用:合并结果集就是把两个select语句的查询结果合并到一起!
合并结果集有两种方式:
union:去除重复记录,例如:SELECT * FROM t1 UNION SELECT * FROM t2;
union all:不去除重复记录,例如:SELECT * FROM t1 UNION ALL SELECT * FROM t2。
- b. 连接查询
连接查询就是求出多个表的乘积,例如t1连接t2,那么查询出的结果就是t1*t2。内连接,等都属于连接查询。
重点语法:SELECT * FROM emp,dept WHERE emp.deptno=dept.deptno; 使用使用主外键来去除无用信息。
- c. 内连接 [inner] join on
语法:SELECT * FROM emp e INNER JOIN dept d ON e.deptno=d.deptno;
内连接的特点:查询结果必须满足条件。
- d. 外连接 (outer join on)
左外连接 left [outer] join
SELECT * FROM emp e LEFT OUTER JOIN dept d ON e.deptno=d.deptno;
左连接是先查询出左表,然后查询右表,右表中满足条件的显示出来,不满足条件的显示NULL。
右外连接 right [outer] join
SELECT * FROM emp e RIGHT OUTER JOIN dept d ON e.deptno=d.deptno;
右连接就是先把右表中所有记录都查询出来,然后左表满足条件的显示,不满足显示NULL。
- e. 自然连接 natural join
大家也都知道,连接查询会产生无用笛卡尔积,我们通常使用主外键关系等式来去除它。而自然连接无需你去给出主外键等式,它会自动找到这一等式:两张连接的表中名称和类型完全一致的列作为条件,例如emp和dept表都存在deptno列,并且类型一致,所以会被自然连接找到!
SELECT * FROM emp NATURAL JOIN dept;
SELECT * FROM emp NATURAL LEFT JOIN dept;
- f. 子查询(非常重要)
- 一个select语句中包含另一个完整的select语句。子查询就是嵌套查询,即SELECT中包含SELECT,如果一条语句中存在两个,或两个以上SELECT,那么就是子查询语句了。
- 子查询出现的位置:where后,作为条件被查询的一条件的一部分; from后,作表;
- 当子查询出现在where后作为条件时,还可以使用如下关键字:any all;
- g. 自连接:自己连接自己,起别名
求7369员工编号、姓名、经理编号和经理姓名
SELECT e1.empno , e1.ename,e2.mgr,e2.ename FROM emp e1, emp e2 WHERE e1.mgr = e2.empno AND e1.empno = 7369;
32. JDBC
1. JDBC概述
JDBC是一种用于执行sql语句的Java API,可以为多种关系型数据库提供统一访问。没有JDBC的时候,如果现在要开发一套系统,使用Java连接MySQL数据库,那么这时候Java程序员需要了解MySQL驱动API,如果使用Java连接Oracle数据库,那么这个时候Java程序员需要了解Oracle数据库驱动API。SUN公司提供一套统一的规范(接口)。然后各个数据库生产商提供这套接口的实现。这套接口规范就是JDBC的规范。
2. JDBC使用入门
JDBC的使用主要分为如下4步:
l 第一步:加载驱动 Class.forName("com.mysql.jdbc.Driver");
l 第二步:获得连接 Connection conn = DriverManager.getConnection("url", "用户名", "密码");
l 第三步:基本操作 先使用连接对象获取执行sql语句的对象,在进行sql语句操作
l 第四步:释放资源 关流,连接流,操作sql语句对象的流,字符集流
3. 抽取JDBC工具类
因为传统JDBC的开发,注册驱动,获得连接,释放资源这些代码都是重复编写的。所以可以将重复的代码提取到一个类中来完成。
4. 配置文件详解
l 配置文件命名规范:文件名.properties; 内容:key=value;
l 使用方法
Properties properties = new Properties(); //创建配置文件对象
properties.load(new FileInputStream("url")); //将配置文件的详细地址使用文件读取流存入
Object value = properties.getProperty("key"); //根据key取出对应的value值
5. JDBC的SQL注入漏洞
l SQL注入漏洞原因:
l 解决方法:
需要采用PreparedStatement对象解决SQL注入漏洞。这个对象将SQL预先进行编译,使用?作为占位符。?所代表内容是SQL所固定。再次传入变量(包含SQL的关键字)。这个时候也不会识别这些关键字。
String sql = "select * from user where username = ? and password = ?"; // 编写SQL语句
pstmt = conn.prepareStatement(sql); // 预编译SQL
pstmt.setString(1, username); // 设置参数1,第一个?处
pstmt.setString(2, password); // 设置参数2,第二个?处
rs = pstmt.executeQuery(); // 执行SQL语句:
6. JDBC的批处理操作
l 执行批处理需在获取连接时的url后加?rewriteBatchedStatements=true语句,不加也能执行,但执行效率低,加上该参数能提高执行效率。执行批处理使用Statement 对象和PreparedStatement对象都可以执行,但建议使用PreparedStatement对象,因为这样只需只需一次预编译就够了。使用Statement 对象批处理可以一次处理增删改查,但使用PreparedStatement对象批处理,只能做其中一种,因为要进行预编译。
l 使用格式:
String sql = "insert into user values (null,?)"; // 编写SQL语句
pstmt = conn.prepareStatement(sql); // 预编译SQL
//设置参数,和添加到批处理,可以多次添加,每次重新设置参数后可以直接添加到批处理中
pstmt.setString(1, "name"+i);
pstmt.addBatch();
pstmt.executeBatch(); // 执行批处理
l 注意问题:
//当使用批处理一次添加上万条数据时,为避免内存溢出,加入判断,当添加进批处理中的数据条数为1000条时,执行
批处理,将添加到批处理中的数据存入数据库中,并清空批处理。
if(i % 1000 == 0){
pstmt.executeBatch(); // 执行批处理:
pstmt.clearBatch(); // 清空批处理:
}
//最后再次执行pstmt.executeBatch()操作,将不满1000的数据存入数据库中
7. JDBC的事务管理
l 事务的概念:事务指的是逻辑上的一组操作,组成这组操作各个逻辑单元要么全都成功,要么全都失败。
l JDBC的事务管理中,主要使用如下3个方法:
void setAutoCommit(boolean args) :传入参数false开启事务,将此连接的自动提交模式设置为给定状态。
void commit() :开始事务,执行操作后,使用此方法提交事务。
void rollback() :撤消在当前事务中所做的所有更改, 此方法一般写在catch中。
8. JDBC的连接池的使用(一般使用C3P0开源连接池)
l 为什么使用连接池:
连接池是装有连接的容器,使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。从内存中获取和归还的效率要远远高于创建和销毁的效率。(提升性能)。
l C3P0开源连接池概述:
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
l C3P0开源连接池的使用(手动设置参数):
// 创建连接池:
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 设置连接参数:
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///web_test4");
dataSource.setUser("root");
dataSource.setPassword("abc");
// 从连接池中获得连接:
conn = dataSource.getConnection();
// 编写SQL:
String sql = "select * from account";
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 执行SQL:
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
}
l C3P0开源连接池的使用(使用配置文件设置参数):
连接池的默认路径为:src目录下的c3p0-config.xml文件。
当使用配置文件设置参数时,其他代码与手动设置参数一样,就其中的设置连接参数代码不需要写了。
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///web_test4</property>
<property name="user">root</property>
<property name="password">abc</property>
</default-config>
<!-- This app is massive! -->
<named-config name="oracle">
</named-config>
</c3p0-config>
在创建连接池时可以使用无参构造也可以使用有参构造,传入一个数据库的名称,会根据这个名称去配置文件中找该名称的数据库配置信息,如果没有找到就会使用默认的数据库配置信息。
9. 开源工具DBUtils的概述
l DBUtils概述:Commons DBUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
l DBUtils中的核心运行类:QueryRunner对象的使用
QueryRunner对象使用主要有2种形式:
一种是只执行CRUD的操作(此时执行构造函数时需传入一个连接池,执行sql语句时可以直接执行):
构造:
QueryRunner(DataSource ds); //执行有参构造,传入一个连接池
方法:
int update(String sql,Object… args); //对数据库进行增删改操作
T query(String sql,ResultSetHandler rsh,Object… args); //对数据库进行查询操作
另外一种是有事务管理的CRUD操作(此时可以执行无参构造函数,但执行sql语句时需传入一个数据库连接):
构造:
QueryRunner(); //执行无参构造
方法:
int update(Connection conn,String sql,Object… args); //执行sql语句时需传入一个连接,通过连接
T query(Connection conn,String sql,ResultSetHandler rsh,Object… args); //来控制事务的状态
l DBUtils中的核心运行类:DbUtils对象的使用:
在DbUtils中,主要有2个控制连接的静态方法需掌握:
commitAndCloseQuietly(Connection conn):提交事务并释放资源(在DBUtils中为归还资源)。
rollbackAndCloseQuietly(Connection conn):回滚并释放资源(在DBUtils中为归还资源)。
10. 开源工具DBUtils的增删改使用
DBUtils的添加操作如下代码:
public void demo1() throws SQLException{
// 创建核心类:QueryRunner,传入连接池
QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
queryRunner.update("insert into account values (null,?,?)", "ddd",10000);
}
DBUtils的删除和修改操作一致,只是sql语句不同而已。
另:使用DBUtils开源工具不用手动关闭资源,该操作已封装到该工具内部,会自动执行。
11. 开源工具DBUtils的查询的代码实现
DBUtils的查询操作使用方法为:T query(String sql,ResultSetHandler<T> rsh,Object… args)方法,具体实现如下:
ArrayHandler,BeanHandler等类均为ResultSetHandler的子类。下述代码中异常均为throw处理。
l ArrayHandler和ArrayListHandler的实现(重点掌握):
原理是:当查询为一条记录时,将这条记录封装到一个Object[]数组当中。当查询为多条记录时,将多条记录封装到一个装有Object[]的List集合中。如下代码为查询多条记录的代码实现:
ArrayHandler的查询代码实现:
public static Object[] ArrayQuery(String sql, Object...parm) throws SQLException {
QueryRunner queryRunner = new QueryRunner(dataSource); //创建QueryRunner查询对象
Object[] query = queryRunner.query(sql, new ArrayHandler(), parm); //进行查询,返回结果
return query;
}
ArrayListHandler的查询代码实现:
public static List<Object[]> ArrayListQuery(String sql, Object...parm) throws SQLException{
QueryRunner queryRunner = new QueryRunner(dataSource); //传入一个连接池创建查询对象
List<Object[]> query = queryRunner.query(sql, new ArrayListHandler(), parm); //进行查询
return query;
}
l BeanHandler和BeanListHandler的实现(重点掌握):
原理为:查询一条记录时,将这条记录封装到一个JavaBean中。查询多条记录时,将这多条记录封装到一个装有JavaBean的List集合中。JavaBean为在Java程序中创建一个成员变量和数据库中的字段一模一样(名称和类型均一样)的类,并生成set和get方法,使用查询时,自动将数据封装成该对象或者List集合。数据库中为Date类型时,在定义JavaBean时可以定义为Date类型,也可以定义为String类型。
BeanHandler的查询代码实现:
public static Stu beanHandler(String sql, Object...parm) throws SQLException {
QueryRunner queryRunner = new QueryRunner(dataSource);
Stu query = queryRunner.query(sql, new BeanHandler<Stu>(Stu.class), parm);
return query;
}
BeanListHandler的查询代码实现:
public static List<Stu> beanListHandler(String sql, Object... parm) throws SQLException {
QueryRunner queryRunner = new QueryRunner(dataSource);
List<Stu> query = queryRunner.query(sql, new BeanListHandler<Stu>(Stu.class), parm);
return query;
}
l MapHandler和MapListHandler的实现:
原理为:将一条记录封装到一个Map集合中,Map的key是列名,Map的value就是表中列的记录值。将多条记 录封装到一个装有Map的List集合中。
public static Map<String, Object> mapHandler(String sql, Object... parm) throws SQLException{
QueryRunner queryRunner = new QueryRunner(dataSource);
Map<String, Object> query = queryRunner.query(sql, new MapHandler(), parm);
return query;
}
MapListHandler的查询代码实现:
public static List<Map<String, Object>> mapListHandler(String sql, Object... parm) throws SQLException {
QueryRunner queryRunner = new QueryRunner(dataSource);
List<Map<String, Object>> query = queryRunner.query(sql, new MapListHandler(), parm);
return query;
}
l ColumnListHandler的实现: 实现原理:将数据中的某列封装到List集合中。(了解)
public static List<Object> columnListHandler(String sql, String lineName, Object... parm) throws SQLException {
QueryRunner queryRunner = new QueryRunner(dataSource);
List<Object> query = queryRunner.query(sql, new ColumnListHandler(lineName), parm);
return query;
}
l ScalarHandler的实现: 实现原理:将单个值封装。(了解)
public static Object scalarHandler(String sql, Object... parm) throws SQLException {
// sql = "select count(*) from stu";
QueryRunner queryRunner = new QueryRunner(dataSource);
Object query = queryRunner.query(sql, new ScalarHandler());
return query;
}
l KeyedHandler的实现: 实现原理:将一条记录封装到一个Map集合中。将多条记录封装到一个装有Map集合的Map集合中。而且外面的Map的key是可以指定的(可以指定为数据库的列名)。(了解)
public static Map<Object, Map<String, Object>> keyedHandler(String sql,String keyName,Object... parm) throws SQLException {
QueryRunner queryRunner = new QueryRunner(dataSource);
Map<Object, Map<String, Object>> query = queryRunner.query(sql, new KeyedHandler(keyName));
return query;
}
12. JDBC自建工具类如下方MyJDBCUtils.txt文件所示,其中将C3P0连接池的获取,连接的获取,关闭资源,DBUtils 中的QueryRunner类的对象的获取,QueryRunner中的各种查询方法等均已写入其中,使用时可以直接使用。
33. 多线程
1. 多线程的概念:
关于线程和进程需了解如下几点:
A. 进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过 程中的程序,并且具有一定独立功能。
B. 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中 是可以有多个线程的,这个应用程序也可以称之为多线程程序。
C. 那什么是多线程呢?即就是一个程序中有多个线程在同时执行。一个程序运行后至少有一个进程,一个进程中 可以包含多个线程。
2. 创建线程的2种方式:
第一种是创建一个类,使用这个类继承Thread类,并重写其中的run方法,再使用start方法开启进程。
第二种是实现Runnable接口,再覆盖其中的run方法,创建Thread类的对象,将Runnable接口的子类对象作为 参数传递给Thread类的构造函数。最后调用Thread类的start方法开启线程。
3. Thread类中的常用方法:
返回值 |
方法名 |
功能介绍 |
构造方法 |
Thread() |
无参构造,分配一个新的 Thread对象。 |
构造方法 |
Thread(Runnable target) |
传入一个Runnable类型的参数,一般传入它的实现类对象。 |
static Thread |
currentThread() |
返回对当前正在执行的线程对象的引用。 |
static void |
sleep(long millis) |
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。 |
String |
getName() |
返回此线程的名称。 |
void |
setName(String name) |
将此线程的名称更改为等于参数 name 。 |
4. 多线程的安全问题和解决办法
a) 安全问题产生的原因:
当多条语句在操作同一个线程专享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。即:当多个线程存在共享数据,并同时执行这个共享数据时,会有问题。
b) 解决办法:(使用synchronized关键字)
有2种解决办法:
第一种是使用synchronized关键字锁住这部分共享的数据,这时当有一个线程在执行这部分代码时,其他线程会在外面等待,当这个线程执行完时才会去执行,括号中的对象只要是所有的线程共享的对象就可以,可以使用字符串,因为字符串是存在缓冲区中的,被所有的线程共享,建议使用”lock”,如下代码所示:
public void run() {
synchronized ("lock") {
//各个线程所共享的数据
}
}
第二种是使用同步函数,使用synchronized关键字来修饰一个函数,这时这个函数中所有的代码都被锁住了,如果这个函数不是静态的,那使用的锁就是this,如果是静态的,那使用的锁就是类名.class。
public synchronized void method() { //代码 }
5. 另还有线程中的通信,死锁,单例设计模式,线程中的通信需了解
34. 网络编程
1. 网络编程概述
通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则;在进行数据传输时,要求发送的数据与收到的数据完全一样,这时,就需要在原有的数据上添加很多信息,以保证数据在传输过程中数据格式完全一致。计算机的网络中,可以分为如下4层:
图:TCP/IP网络模型 |
应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。 |
传输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。 |
|
网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。 |
|
链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。 |
在网络传输中,首先需要自己在网络中的地址(即IP),关于自己的地址JDK中封装成了InetAddress类,方法如下:
static InetAddress |
getLocalHost() |
返回本地主机。 |
static InetAddress |
getByName(String host) |
在给定主机名的情况下确定主机的 IP 地址。 |
String |
getHostName() |
获取此 IP 地址的主机名。 |
String |
getHostAddress() |
返回 IP 地址字符串(以文本表现形式)。 |
2. UDP协议
(1) UDP协议的通信方式:UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。
(2) UDP协议通信详解:
UDP协议通信就像是货运公司在两个码头间发送货物一样,在码头发送和接收货物时都需要使用集装箱来装载货物。所以UDP协议通信就需要2个码头和将数据打包的集装箱,在JDK中用来发送货物的码头用DatagramSocket(套接字)类表示,用来封装货物的集装箱用DatagramPacket(数据包)类来表示。
① DatagramSocket(套接字)中的常用方法如下:
构造方法 |
DatagramSocket() |
构造数据报套接字并将其绑定到本地主机上任何可用的端口。 |
构造方法 |
DatagramSocket(int port) |
创建数据报套接字并将其绑定到本地主机上的指定端口。 |
void |
receive(DatagramPacket p) |
从此套接字接收数据报包。 |
void |
send(DatagramPacket p) |
从此套接字发送数据报包。 |
② DatagramPacket(数据包)中的常用方法如下:
构造函数 |
DatagramPacket(byte[] buf, int length) |
构造 DatagramPacket,用来接收长度为 length 的数据包。 |
构造函数 |
DatagramPacket(byte[] buf, int length, InetAddress address, int port) |
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。 |
InetAddress |
getAddress() |
返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。 |
byte[] |
getData() |
返回数据缓冲区。 |
int |
getLength() |
返回将要发送或接收到的数据的长度。 |
int |
getPort() |
返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。 |
3. TCP协议
(1) TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。所以,在JDK中,提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端,一个是Socket类,用于表示客户端。
(2) Socket中的常用方法:
构造方法 |
Socket(InetAddress address, int port) |
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 |
构造方法 |
Socket(String host, int port) |
创建一个流套接字并将其连接到指定主机上的指定端口号。 |
int |
getPort() |
该方法返回一个int类型对象,该对象是Socket对象与服务器端连接的端口号 |
InetAddress |
getLocalAddress() |
该方法用于获取Socket对象绑定的本地IP地址,并将IP地址封装成InetAddress类型的对象返回 |
void |
close() |
该方法用于关闭Socket连接,结束本次通信。在关闭socket之前,应将与socket相关的所有的输入/输出流全部关闭,这是因为一个良好的程序应该在执行完毕时释放所有的资源 |
InputStream |
getInputStream() |
该方法返回一个InputStream类型的输入流对象,如果该对象是由服务器端的Socket返回,就用于读取客户端发送的数据,反之,用于读取服务器端发送的数据 |
OutputStream |
getOutputStream() |
该方法返回一个OutputStream类型的输出流对象,如果该对象是由服务器端的Socket返回,就用于向客户端发送数据,反之,用于向服务器端发送数据 |
boolean |
isInputShutdown() |
返回是否关闭套接字连接的半读状态 (read-half)。 |
boolean |
isOutputShutdown() |
返回是否关闭套接字连接的半写状态 (write-half)。 |
(3) ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行。
常用方法如下:
构造方法 |
ServerSocket(int port) |
创建绑定到特定端口的服务器套接字。 |
Socket |
accept() |
侦听并接受到此套接字的连接。 |
InetAddress |
getInetAddress() |
返回此服务器套接字的本地地址。 |
4. 示例代码如下:
35. 反射
1. 反射介绍:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能称为java语言的反射机制。
2. 字节码文件获取的三种方式(会返回一个字节码对象):
(1) 对象名.getCalss(); // 次方法来自于Object 对象已经存在的情况下, 可以使用这种方式
(2) 类名.class // 类名.class这是一个静态的属性, 只要知道类名, 就可以获取
(3) Class.forName(“com.itheima_01.Student”);
// 通过Class类中的静态方法, 指定字符串, 该字符串是类的全类名(包名+类名)
// 此处将会抛出异常都系 ClassNotFoundException 防止传入错误的类名
3. 通过获取构造方法创建对象:
当使用Class类的静态方法forName()获取到Class对象后,可以通过此对象来创建具体类的对象。
Class aClass = Class.forName("com.itheima.test01.Demo02");
(1) 可以直接使用aClass调用newInstance()方法, 但这个方法只能通过空参的构造方法创建对象。
(2) Constructor<?>[] getConstructors():可以使用aClass调用getConstructors()方法,此方法返回的是一个数组。
(3) Constructor<T> getConstructor(Class<?>... parameterTypes) :可以通过此方法获取对应的构造函数的对象。如下:
Class aClass = Class.forName("com.itheima.test01.Demo02"); //获取字节码对象
Constructor constructor = aClass.getConstructor(String.class,int.class); //获取构造函数对象
Object obj = constructor.newInstance("zhangsan",54); //通过构造函数对象创建相应的目标对象
System.out.println(obj); //将此对象输出
注意:以上方法只适应于public修饰的,构造方法均为public修饰。
4. 通过反射获取和使用成员变量:
Class aClass = Class.forName("com.itheima.test01.Demo02"); //通过Class类中的静态方法获取该类的字节码 |
可以通过字节码对象获取类中的属性,会返回一个字段对象,但只能是公有,一般不用: Field[] fields = aClass.getFields(); //通过字节码对象调用getFiles()方法可以获取该类中公有的属性的数组 Field age = aClass.getField("age"); //获取指定的名称的字段的对象(只能是公有) |
通过字节码对象获取类中的属性,会返回一个字段对象,所有属性,包括私有,经常使用: Field[] declaredFields = aClass.getDeclaredFields(); //获取该类中所有的属性的集合(包括私有) Field age1 = aClass.getDeclaredField("age"); //获取指定的名称的字段的对象(包括私有) |
通过字节码获取的字段对象可以使用set和get方法传入一个对象,来使用这个对象中的该属性,但只能是公有 age1.set(stu,"abc"); Object o = age1.get(stu); |
AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。 当使用字段对象调用setAccessible(boolean flag) 方法后,这个字段就可以访问该对象中的私有属性了。 如:Field f = aClass.getDeclaredField("name"); f.setAccessible(true); 这样f对象就可以访问私有属性了。 |
5. 通过反射获取和使用成员方法:
Class clazz = Class.forName("com.heima.Student"); // 获取字节码对象
Object stu = clazz.newInstance(); // 创建学生对象
// 暴力反射获取方法,如果方法需传入参数,需在后面加上传入参数的字节码对象,如需传入String类型,就String.class
Method method = clazz.getDeclaredMethod("method",parm...);
// 让jvm不检查权限
method.setAccessible(true);
// 执行方法, 如果该方法需传入参数,需在后面加上具体传入的参数来执行方法,如果该方法有返回值,需创建一个Object 对象来接收此返回值。
method.invoke(stu,parms...);
6. BeanUtils
(1) BeanUtils的概述:
之前我们使用的类都是来自Java编写好的源代码,而这个BeanUtils却是一个叫做Apache的组织编写。那么这个组织编写的代码当中, 有一个系列可以很方便的提高我们今后的开发效率。这个系列为Commons, BeanUtils就是这个系列中的一个。
(2) BeanUtils的使用:
第一步:导入两个jar包:commons-beanutils-1.8.3.jar和commons-logging-1.1.1.jar
第二步:将jar包Build path 配置到当前的classpath环境变量中
(3) BeanUtils中常用的三个方法:
setProperty(Object bean, String name, Object value) :用来设置Bean对象的属性值。
getProperty(Object bean, String name) :用来获取Bean对象的属性值。
populate(Object bean, Map properties):对Bean对象的属性值进行一次性设置。
(4) 使用BeanUtils时的注意事项:
三个方法底层是通过反射实现, 而且反射操作的是setXxx方法和getXxx方法;所以编写JavaBean的时候一定要注意格式。使用BeanUtils时操作的Bean类必须要有公有的set和get方法。