Java总结
链接放在评论里了
基础操作
cls清屏
dir查看当前目录下文件
exit 退出doc窗口
del删除
- del *.class 删除以class结尾的文件
查看IP地址 ipconfig
- ipconfig all 查看更详细的信息
是否正常通信(可以网速检测) ping
-
ping IP地址
-
ping 域名
-
加 -t 一直ping
-
ping www.baidu.com -t
cd切换目录
- 切换盘符: D/
- cd . . 上一目录
java
- 运行
javac
- 编译
基础格式
格式
public class A{
public static void main(String[] args){
System.out.println(“文本”);
}
}
- public声明的类名要与文件名一致,其余的类不要求
一个类中可以有多个类,但是只能有一个public声明的类
标识符
- 可以自己命名的单词
- 与C++相似
类名尽量不要相同(类似A与a)
命名规范
变量名、方法名首字母小写,后面每个单词首字母大写
类名、接口名每个单词首字母大写
注释
Javadoc注释
可以使用Javadoc来生成特定文档
/**
*
*/
基础语法
数据类型
字节型 byte 1
布尔型 boolean 1
其它与C++相似
println
输出括号中的内容println()并换行
而print()并不换行
输出单个的符号:列如 ’
在前加一个反斜杠
列如: 输出 " 使用 "
byte类型的强制转换
整数类型字面常量没有超过byte的取值范围,则可以直接赋值给byte类型变量
其它类型相似的情况相似
boolean类型
在java 中boolean只有true、false两种类型
类型转换规则
一、八种数据类型中,除boolean类型不能转换,剩下七种类型之间都可以经行转换
二、如果整数型字面量没有超出byte、short、char的取值范围,可以直接转换
三、小容量到大容量转换称为自动类型转换,容量从小到大的排序为:
byte<short(char)<int<long<float<double
short和char类型都占两字节,但char可以表示更大的正整数
四、强制转换符需添加强制转换符
五、byte、short、char类型混合运算时,先转换成int类型再做运算
六、多种数据类型混合运算时,先各自转换成容量最大的一种再做运算
逻辑运算符
Java中(运算符两边只能为boolean类型):
& 逻辑与(相似C++&&)
| 逻辑或(相似C++||)
! 逻辑非(相似C++!)
&& 短路与
|| 短路或
短路与: &&与&运算结果相同,但会发生短路,&&左边表达式为假时,右边表达式则不会执行
短路或:与短路与类似
赋值运算符
使用扩展赋值类型如:+=
永远都不会改变运算结果类型
列:
byte x+=1; 可以编译通过
byte x=x+1; 编译可以通过
x+=1;等同于x=(byte)(x+1);
自加运算符
在java中:
k=k++;
等同于:
int tmp=k;
k++;
k=temp;
k的值仍然是10
Java中的“+”号
当+号运算符两边都是数组时:求和
当+号运算符两边任意一边时字符串类型,那么这个+会进行字符串拼接操作
注意运算为从左自右,括号优先
类型默认值
类型 默认值
byte 0
short 0
int 0
long 0L
float 0.0F
double 0.0
boolean false
char \u0000
引用数据类型 null
语法
输出
1
输入
java.util.Scanner s=
new java.util.Scanner(System.in);
int i=s.nextInt();
char c=s.next();
if
在Java中if(i=10)编译会报错
i=10结果为int 类型,不能转换为boolean
switch
switch本质上只支持int和String类型,但是byte,short,char也可以使用在switch中;会发生自动类型转换。
其它特性和C++相似
for
与C++相似
break
特殊用法:
a:for( , , )
{
b:for( , , )
{
}
break a;
}
结束外层循环
异常
常见异常
-
java.lang.ClassCastException:
类型转换异常
-
java.lang.NullPointerException:
空指针异常
-
ArrayIndexOutOfBoundsException
数组越界
-
NumberFrmatException
数字格式化异常
要求是数字,但输入的不是数字
IDEA
快捷键
-
psvm:快速生成main方法
-
sout:快速生成System.out.println( )
-
注释
- 单行:ctrl+/
- 多行:ctrl+shift+/
-
复制一行: ctrl+d
-
纠错
- alt+回车
-
查看类的属性
- ctrl+F12
子主题 2
进阶
final
表示最终的,不可变的
-
修饰类
修饰的类将无法被继承
final class A{
} -
修饰方法
方法无法被覆盖,重写
-
修饰变量
一但赋上值,就不能重新赋值
(只能赋值一次) -
修饰引用
引用也变量,一但赋上值,就不能重新赋值(只能赋值一次)
引用指向对象后不能重新再指向别的对象,但可以修改指向对象内的数据
-
修饰实列变量
一但赋上值,就不能重新赋值
(只能赋值一次)final修饰的实列变量,系统不会赋默认值,要求手动赋值
final修饰的实列变量,应该在前添加static ,变为静态,节省内存
-
static final
static final修饰的变量称为常量,变量名应该全部大写
常量存储在方法区
在类加载时初始化
-
抽象类和接口
-
抽象类
抽象类:类和类之间有共同特征,将这些具有共同特征的类再进一步抽象形成了抽象类。
由于类本身是不存在的,所以抽象类无法创建对象
(半抽象的)-
类型
也属于引用类型
-
语法
语法:
[修饰符列表] abstract class 类名{
类体;
} -
使用
被子类继承,从而实列化
final和abstract不能同时使用
-
抽象方法
表示没有实现的方法,没有方法体的方法
public abstract void sum( );
特点:
没有方法体,一分号结尾
有abstract修饰抽象方法必须出现再抽象类中,抽象类中不一定有抽象方法
-
非抽象类继承抽象类必须将抽象方法实现
-
-
接口
接口也是一种引用数据类型
接口是完全抽象的
(接口是特殊的抽象类)-
基础语法
[修饰符列表] interface 接口名{
}
接口支持多继承
接口中只有两部分:
1、常量
2、抽象方法接口中所有元素都是public修饰(都是公开的)
接口中public abstract可以省略
接口中public static final可以省略
接口中的方法都是抽象方法,所以接口中的方法不能有方法体 -
接口的作用
接口在开发的作用,类似于多态在开发中的作用
接口是纯抽象,有了接口就有了可插拔。可插拔表示扩展力很强。
-
重点
当一个非抽象的类实现接口的话,必须将接口中所有的抽象方法全部实现(覆盖、重写)
-
类与接口之间叫实现
关键字:implements
interface MyMath{
…
}
class M implements MyMath{
…
}类可以多实现接口
继承时转型规则不适用于接口的转型(向下转型)
-
extends和implements同时出现
class Cat extends Animal implements Flyable{
…
}接口通常提取的是行为动作
先写extends 后写 implements
-
-
抽象类和接口的区别(语法上)
抽象类是半抽象的
接口是完全抽象的抽象类中有构造方法
接口中没有构造方法接口和接口之间支持多继承
类和类之间只能单继承一个类可以同时实现多个接口
一个抽象类只能继承一个类(单继承)接口中只允许出现常量和方法
包机制package
-
作用
为了方便程序的管理
不同功能的类分为别放在不同的包下。(按照功能划分) -
语法
package +包名
package com.bjpowernode.javase.chapter17;package只能出现在Java源代码的第一行
-
命名规范
公司域名倒序+项目名+模块名+功能名
-
使用
类名不在是class A中的A
而是com.bjpowernode.javase.chapter17.HelloWorld;编译:
javac -d .HelloWorld.java
javac:负责编译
-d :带包编译
. :代表编译后生成的东西放在当前目录下(点表示当前目录)
HelloWorld.java:表示被编译的Java文件名创建new 一个对象
com.bjpowernode.javase.chapter17.HelloWorld hw=new com.bjpowernode.javase.chapter17.HelloWorld( );
import机制
-
语法
创建new 一个对象
com.bjpowernode.javase.chapter17.HelloWorld hw=new com.bjpowernode.javase.chapter17.HelloWorld( );也可以写成
Text1{HelloWorld hw=new HelloWorld( );
}
原因 HelloWorld和Text1在同一个package下引入import:
package com;
//需要将需要的类引入
import com.bjpowernode.javase.
chapter17.HelloWorld;另一种写法:
package com;
//需要将需要的类引入
import com.bjpowernode.javase.
chapter17.* ; -
什么时候使用
A类中使用B类
A和B类都在同一个包下。不需要import
A和B类不在同一个包下。需要使用importimport只能写在package之下
java.lang.*;这个包下的类不需要使用Import;
-
Scanner的其它写法
package com.bjpowernode.javase.
chapter17;import java.util.Scanner;
//(或者import java.util.*);public class Text{
public static void main(String [] args){
Scanner s = new Scann
er(System.in);
String str = s.next();
…
}
}
访问权限
public>protected>默认>private
-
private
只能在本类中访问
-
protected
表示只能在本类、同包、子类中访问
-
public
任何位置都可以
-
默认
只能在本类,以及同包下访问
Object
-
负责对象克隆
protected Object clone( )
-
获取对象哈希值
hashCode
public int hashCode()
返回此合成的哈希码。
覆盖:
类 Object 中的 hashCode
返回:
此合成的哈希码。 -
判断两个对象是否相等
判断中引用数据类型使用equals
(基本数据类型使用==)boolean equals (Object obj)
public boolean equals(Area other)
测试两个 Area 对象的几何形状是否相等。如果参数为 null,则此方法将返回 false。参数:
other - 将与此 Area 比较的 Area
返回:
如果两个几何形状相等,则返回 true;否则返回 false。常用格式:
public boolean equals(Object obj){
if(obj == null || !(obj instanceof Student))return false;
if(this == obj ) return true;
Student s = (Student)obj;
if(this.no==s.no&&…){
return true;
}
return false; -
将对象转换成字符串
String toString( )
getClass().getName() + ‘@’ + Integer.toHexString(hashCode())
返回该对象的字符串表示。
通常,toString 方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明但易于读懂的信息表达式。建议所有子类都重写此方法。
-
垃圾回收器负责调用的方法
finalize
protected void finalize()
释放与 ICC_Profile 对象关联的资源。这个方法不需要程序员手动调用,JVM的垃圾回收器负责调用这个方法
当一个java对象即将被垃圾回收器回收的时候,回收器负责调用建议启动垃圾回收器:(不一定启动)
System.gc( );
内部类
在类的内部又定义了一个新的类
分类:
静态内部类:类似于静态变量
实列内部类:类似于实列变量
局部内部类:类似于局部变量
可读性很差
-
匿名内部类
属于局部内部类
部建议使用;其可读性太差,无法重发使用
-
定义
定义匿名内部类
例子:1 public class HelloWorldAnonymousClasses {
2
3 /**
4 * 包含两个方法的HelloWorld接口
5 */
6 interface HelloWorld {
7 public void greet();
8 public void greetSomeone(String someone);
9 }
10
11 public void sayHello() {
12
13 // 1、局部类EnglishGreeting实现了HelloWorld接口
14 class EnglishGreeting implements HelloWorld {
15 String name = “world”;
16 public void greet() {
17 greetSomeone(“world”);
18 }
19 public void greetSomeone(String someone) {
20 name = someone;
21 System.out.println("Hello " + name);
22 }
23 }
24
25 HelloWorld englishGreeting = new EnglishGreeting();
26
27 // 2、匿名类实现HelloWorld接口
28 HelloWorld frenchGreeting = new HelloWorld() {
29 String name = “tout le monde”;
30 public void greet() {
31 greetSomeone(“tout le monde”);
32 }
33 public void greetSomeone(String someone) {
34 name = someone;
35 System.out.println("Salut " + name);
36 }
37 };
38
39 // 3、匿名类实现HelloWorld接口
40 HelloWorld spanishGreeting = new HelloWorld() {
41 String name = “mundo”;
42 public void greet() {
43 greetSomeone(“mundo”);
44 }
45 public void greetSomeone(String someone) {
46 name = someone;
47 System.out.println("Hola, " + name);
48 }
49 };
50
51 englishGreeting.greet();
52 frenchGreeting.greetSomeone(“Fred”);
53 spanishGreeting.greet();
54 }
55
56 public static void main(String… args) {
57 HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
58 myApp.sayHello();
59 }
60 } -
语法
匿名类是一个表达式,匿名类的语法就类似于调用一个类的构建函数(new HelloWorld()),除些之外,还包含了一个代码块,在代码块中完成类的定义,见以下两个实例:
案例一,实现接口的匿名类:1 HelloWorld frenchGreeting = new HelloWorld() {
2 String name = “tout le monde”;
3 public void greet() {
4 greetSomeone(“tout le monde”);
5 }
6 public void greetSomeone(String someone) {
7 name = someone;
8 System.out.println("Salut " + name);
9 }
10 };
-
数组
1、java中的数组是一种引用数据类型,父类是Object
2、存储在堆中
3、数组一但创建,在Java中长度不可变
4、所有数组对象都有length属性(Java自带),用以获取数组中元素的个数
5、元素类型统一
6、内存地址连续
7、数组中首元素的地址作为整个数组的地址
优点“便于检索,效率高
缺点:增加,删除数组元素时效率低
-
main方法中的String数组
JVM负责调用
main 中的String数组是留给用户的,用户可以在控制台上输入参数,这个参数会被自动转换为字符串数组调用:java ArrayText05 abc def xyz
JVM会将abc def xyz按照空格分离,自动放到main 方法中的String[ ]args中用以接收用户输入的参数
-
一维数组
-
定义
int [ ] array1;
double[ ] array2;
… -
初始化
-
静态初始化
int [ ]array={100,2100,30055};
-
动态初始化
int[ ] array = new int [5];
(5个元素,默认值为0)
-
-
扩容
Java中的扩容是将小容量的数组中数据拷贝到大的数组
效率低
System.arraycopy(五个参数)
System.arraycopy(数组源src,从源的第i个位置,目标数组dest,复制到目标数组的第i个位置开始,复制长度)
-
-
二维数组
-
定义
二维数组是特殊的一维数组(三维是特殊的二维数组)
int[ ] [ ] a = {
{20,30,56}
{22,5,1,63,5,2,5}
{2,5,7}
} -
初始化
-
静态初始化
类似一维的
-
动态初始化
int[ ] array = new int [5];
(5个元素,默认值为0)
-
-
-
Arrays工具类
-
java.util.Arrays;
所有方法都是静态的,直接使用类名调用
-
Arrays.sort()
int[] array={1,2,52,36};
Arrays.sort(array);
for (int i = 0; i <4 ; i++) {
System.out.println(array[i]);
} -
Arrays.binarySearch()二分查找
int[] array={1,2,52,36};
int k=Arrays.binarySearch(array,52);
System.out.println(k);
-1表示没有查找到
-
-
String字符串
-
概括
public final class String
extends Object
implements Serializable, Comparable, CharSequence
String 类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。
字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。例如:String str = "abc";
等效于:
char data[] = {'a', 'b', 'c'}; String str = new String(data);
存储在方法区中的“字符串常量池”中
垃圾回收器是不会回收常量的
-
使用new String方法创建
String s1 = “asdfs” + “xy”;
会创建新的一个字符串"asdfsxy"
s1=“asdfsxy”;String s2 =new String( “xy”);
凡是双引号括起来的都在字符串常量池中有一份
new 对象的时候一定在堆中开阔空间
"xy"来自方法区中的字符串常量池中与s1时的“xy"是同一个 -
同一字符串地址
String s1=“sddsff”;
String s2=“sddsff”;
"sddsff"是方法区中字符常量池中是同一份
s1,s2储存的地址相同如果是new的
String s1=new String(“sddsff”);
String s2= new String (“sddsff”);
s1,s2储存的地址不同储存两个对象的地址,但new出来的两个对象储存的地址相同; -
比较两个字符串
使用equals
特殊:System.out.println(“text”.equals(k));
(建议这么用,k是字符串) -
例题
String s1 = new String( “hello”);
String s2 = new String( “hello”);一共3个对象
方法区字符串常量池中一个:”hello“
堆中有两个String 对象 -
常用构造方法
String s1 = “hello”;
byte[ ] bytes = {97,98,99};//97是a,98是b,99是c
String s2 = new String( bytes);将byte数组中的一部分转换为字符串
String s3 = new String(bytes,1,2)//bc -
charAt
charAt(int index)
返回指定索引处的 char 值。char c =“abc”.charAt(1);//b
-
contains(CharSequence s)
contains(CharSequence s)
当且仅当此字符串包含指定的 char 值序列时,返回 true。
判断前面字符串是否含有后面的子字符串 -
endsWith(String suffix)
endsWith(String suffix)
测试此字符串是否以指定的后缀结束。 -
getBytes
getBytes
public byte[] getBytes(Charset charset)
使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。
此方法总是使用此字符集的默认替代 byte 数组替代错误输入和不可映射字符序列。如果需要对编码过程进行更多控制,则应该使用 CharsetEncoder 类。参数:
charset - 用于编码 String 的 Charset
返回:
所得 byte 数组byte[ ] bytes= “adsaf”.getBytes( );
-
indexOf
indexOf
public int indexOf(int ch)
返回指定字符在此字符串中第一次出现处的索引。如果在此 String 对象表示的字符序列中出现值为 ch 的字符,则返回第一次出现该字符的索引(以 Unicode 代码单元表示)。对于 0 到 0xFFFF(包括 0 和 0xFFFF)范围内的 ch 的值,返回值是
this.charAt(k) == ch为 true 的最小 k 值。对于其他 ch 值,返回值是
this.codePointAt(k) == ch为 true 最小 k 值。无论哪种情况,如果此字符串中没有这样的字符,则返回 -1。
参数:
ch - 一个字符(Unicode 代码点)。
返回:
在此对象表示的字符序列中第一次出现该字符的索引;如果未出现该字符,则返回 -1。lostIndexOf取最后一次
-
isString
isEmpty
public boolean isEmpty()
当且仅当 length() 为 0 时返回 true。返回:
如果 length() 为 0,则返回 true;否则返回 false。 -
toCharArray
toCharArray
public char[] toCharArray()
将此字符串转换为一个新的字符数组。返回:
一个新分配的字符数组,它的长度是此字符串的长度,它的内容被初始化为包含此字符串表示的字符序列。 -
trim
trim
public String trim()
返回字符串的副本,忽略前导空白和尾部空白。
如果此 String 对象表示一个空字符序列,或者此 String 对象表示的字符序列的第一个和最后一个字符的代码都大于 ‘\u0020’(空格字符),则返回对此 String 对象的引用。
否则,若字符串中没有代码大于 ‘\u0020’ 的字符,则创建并返回一个表示空字符串的新 String 对象。
否则,假定 k 为字符串中代码大于 ‘\u0020’ 的第一个字符的索引,m 为字符串中代码大于 ‘\u0020’ 的最后一个字符的索引。创建一个新的 String 对象,它表示此字符串中从索引 k 处的字符开始,到索引 m 处的字符结束的子字符串,即 this.substring(k, m+1) 的结果。
此方法可用于截去字符串开头和末尾的空白(如上所述)。返回:
此字符串移除了前导和尾部空白的副本;如果没有前导和尾部空白,则返回此字符串。 -
valueOf
静态的
将非字符串转换为字符串 -
StringBuffer拼接字符串
StringBuffer ss= new StringBuffer();
ss.append(“s”);
ss.append(“j”);
System.out.println(ss);append方法
线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
StringBuffer是线程安全的
StringBuilder是非线程安全的 -
split
split
public String[] split(CharSequence input)
围绕此模式的匹配拆分给定输入序列。
此方法的工作方式类似于使用给定的输入序列和限制参数零调用两参数 (java.lang.CharSequence, int) split} 方法。因此,得到的数组中不包括尾部空字符串。
例如,输入 “boo:and:foo” 将产生以下结果及表达式:
Regex
Result
: { “boo”, “and”, “foo” } o { “b”, “”, “:and:f” }参数:
input - 要拆分的字符序列
返回:
根据围绕此模式的匹配来拆分输入后所计算的字符串数组String str=“adfa/dsagfa/dasf”;
String[] p=str.split("/"); -
parseInt字符串转化为int
int reValue=Integer.parseInt(“123”)
基础类型对应的8个包装类
-
byte 包装类型:java.long.Byte
-
short 包装类型:java.long.Short
-
int 包装类型:java.long.Integer
构造方法:
Intefer(int )
Integer(String)
(其它包装类类型的也有相应的构造方法)通过访问包装类常量访问最大最小值
列:
int类型的:Integer.MAX_VALUE
Integer.MIN_VALUE -
long 包装类型:java.long.Long
-
float 包装类型:java.long.Float
-
double 包装类型:java.long.Double
-
boolean 包装类型:java.long.Boolean
-
char 包装类型:java.long.Character
拆箱、装箱
-
拆箱
将引用数据类型–(转换为)->基本数据类型
在Number中包含一些方法可以实现-
自动拆箱
Integer x=9;
int y=x;不会触发自动拆箱机制
Integer a=1000;
Integer b=1000;
ab//false
a、b保存的是两个不同的内存地址Integer a=1000;相当于:
Integer a=new Integere(1000);-
特殊
Java中为了提高执行效率,将[-128,127]之间所有的包装对象提前创建好,放到了一个方法区的“整数型常量池"中了;目的是不用再new了,直接从整数性常量池中取;
Integer a=127;
Integer b= 127;
a==b//true
-
-
-
装箱
将基本数据型–(转换为)->引用数据类型
-
自动装箱
Integer x=10;
-
日期
-
获取系统当前时间
Date nowTime= new Date();
System.out.println(nowTime);
//格式化日期格式
//在日期格式中,除了y M d H m S s 这些字符不能随意写,其余的都可随意组织
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd:mm:ss SSS”);String nowTimeStr = sdf.format(nowTime);
System.out.println(nowTimeStr);导入包:
import java.text.SimpleDateFormat;
import java.util.Date; -
将字符串转换为日期
String time =“2008-08-08 08:08:08 888”;
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd:mm:ss SSS”);
Date datetime =sdf.parse(time);-
出现异常
错误:(18, 34) java: 未报告的异常错误java.text.ParseException; 必须对其进行捕
将主方法:
public static void main(String[] args)throws Exception {
-
-
获取时间间隔
列:
获取自1970年1月1日00:00:000到当前系统时间的总毫秒数-
作用:统计一个方法执行所耗费的时长
long begin =System.currentTimeMillis();
print();
long end =System.currentTimeMillis();
System.out.println(“时长:”+(end-begin));
}
public static void print(){
for (int i = 0; i <10000 ; i++) {
System.out.println(“i=”+i);
}
}
-
数字
-
格式化
格式有:
#代表任意数字
, 代表千分位
. 代表小数点
0 代表不够时补0列:
###,###.##
表示:加入千分位,保留两个小数//java.text.DecimalFormat专门负责数字格式化
DecimalFormat df=new DecimalFormat("###,###.##");
String s=df.format(1234.56);
System.out.println(s);//1,234.56
-
大数据BigDecimal
属于大数据,精度极高,不属于基本数据类型
BigDecimal b1=new BigDecimal(100);
BigDecimal b2=new BigDecimal(200);
//求和
BigDecimal b3=b1.add(b2);
System.out.println(b3);
随机数
-
例子
//创建随机数对象
Random random =new Random();
//随机产生一个int类型取值范围内的数字
int num = random.nextInt();
System.out.println(num);//产生[0,100]之间的随机数
num =random.nextInt(101);
System.out.println(num);
枚举类型enum
-
定义
enum 枚举名 {
枚举值1,枚举值2
} -
使用场景
1、一个方法的返回有多种情况,boolean只能描述两个,使用枚举能够描述多种情况
2、例如星期
异常exception
-
异常在Java中以类存在
每一个异常类都可以创建异常对象
-
处理方式
- 两种方式
第一种方式:在声明的位置上继续使用:throws,来完成异常的上抛,抛给调用者
public static void main(Stringp[ ] args) throws ClassNotFoundExeption{
doSome( );
}
第二种方式:try ..cath进行捕捉
public static void main(String[ ] args){
try{
doSome( );
catch (ClassNotFoundExeption e){
e.printStackTrace( );
}
}
public ststic void doSome( ) throws ClassNotFoundExeption{
System.out.println("...");
}
-
try catch
一个try可以对应多个catch
try {
FileInputStream fis = new FileInputStream(“D:\Folders.2\算法1\排序\排序算法代码\简单选择排序”);
fis.read();
System.out.println(“文件读取成功”);
}catch(FileNotFoundException e){
System.out.println(“文件不存在”);
}catch (IOException e){
System.out.println(“文件报错”);
}catch写多个时,从上到下,必须遵守从小到大
catch里用或者|
catch(FileNotFoundException e|FileNotFoundException e) -
如何选择上报和捕捉
希望调用者来处理,选择throws上包
其它选择捕捉 -
异常对象常用方法
-
获取简单的描述信息 String msg = e.getMessage( );
获取简单的描述信息 String msg = exception.getMessage( );
-
打印异常追踪的堆栈信息 e.printStackTrace( );
打印异常追踪的堆栈信息 exception.printStackTrace( );
采用异步线程的方式打印
可以用来快速调试信息,(看自己写的部分就行,从上往下)
at java.base/java.io.FileInputStream.open(FileInputStream.java:211)
at java.base/java.io.FileInputStream.(FileInputStream.java:153)
at java.base/java.io.FileInputStream.(FileInputStream.java:108)
at YiChang.folderText01.main(folderText01.java:11)
-
-
finally
- 用法
在finally子句中的代码是最后执行的额,并且一定会执行的(即使在try中有return,但是System.exit除外),即使try做语句块中的代码出现的异常
必须和try一起出现
(close()方法有异常采用,捕捉的方法)
FileInputStream fis=null;
try {
fis = new FileInputStream("D:\\Folders.2\\算法1\\排
\\排序算法代码\\简单选择排序");
fis.read();
System.out.println("文件读取成功");
}catch(FileNotFoundException e){
e.printStackTrace( );
System.out.println("文件不存在");
}catch (IOException e){
System.out.println("文件报错");
}finally {
if(fis !=null){
try {
fis.close();
}catch (IOException e){
e.printStackTrace();
}
}
}可以用于关闭流
try 和finally,没有catch也可以
-
自定义异常
- 步骤
第一步:
编写一个类继承Exception(多发生时选)或者RumtimeException
第二步:
public class MyException extends Exception{//编译时异常
//写一个无参构造函数和一个有参数构造函数
//有参数列:
public MyException(String s){
super(s);
}
}
public class MyException extends RumtimeException{//运行时异常
}
- 手动抛异常
public void push(Object obj) throw MyException {
throw new MyException("异常");
}
- 实际应用
- 异常类
package STACK;
public class MyException extends Exception{
public MyException(){
}
public MyException(String s){
super(s);
}
}
- 栈
package STACK;
public class stack {
private Object[] element;
public Object[] getElement() {
return element;
}
public void setElement(Object[] element) {
this.element = element;
}
public int top;
public stack() {
this.element=new Object[10];
}
public stack(Object[] element) {
this.element = element;
}
public void push(Object obj)throws MyException{
if(top>=element.length){
throw new MyException("压栈失败栈已满");
}
System.out.println("元素"+obj+"压栈成功");
element[top]=obj;
top++;
}
public Object pop()throws MyException{
top--;
if (top<0){
throw new MyException("弹栈失败栈已空");
}
System.out.println("元素"+element[top]+"弹栈成功");
return element[top];
}
}
- 测试
package STACK;
import STACK.stack;
public class Text01 {
public static void main(String[] args) {
stack stack1=new stack();
try{
stack1.push(new Object());
stack1.push(new Object());
stack1.push(new Object());
stack1.push(new Object());
stack1.push(new Object());
stack1.push(new Object());
stack1.push(new Object());
stack1.push(new Object());
stack1.push(new Object());
stack1.push(new Object());
//满
stack1.push(new Object());
stack1.push(new Object());
}catch (MyException e){
//e.printStackTrace();
//输出异常的简单信息
System.out.println(e.getMessage());
}
try {
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
//空
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
}catch (MyException e){
System.out.println(e.getMessage());
}
}
}
- 重写后的方法不能比之前的方法抛出更多的异常,但可以更少
- throws在方法声明位置上使用,表示上报异常信息给调用者
- throw手动抛出异常
集合
集合是一个容器,数组也是一个集合
不能直接存储基本数据类型,和Java对象;集合中存储的Java对象的内存地址(或者说是引用)
所有的集合类和集合接口都在Java.until包下
-
概述
List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口
Set下有HashSet,LinkedHashSet,TreeSet
List下有ArrayList,Vector,LinkedList
Map下有Hashtable,LinkedHashMap,HashMap,TreeMap
Collection接口下还有个Queue接口,有PriorityQueue类 -
与数组比较
数组不是面向对象的,存在明显的缺陷,集合弥补了数组的缺点,比数组更灵活更实用,而且不同的集合框架类可适用不同场合。如下:
数组能存放基本数据类型和对象,而集合类存放的都是对象,集合类不能存放基本数据类型。数组和集合存放的对象皆为对象的引用地址。
数组容易固定无法动态改变,集合类容量动态改变。
数组无法判断其中实际存有多少元素,length只告诉了数组的容量,而集合的size()可以确切知道元素的个数
集合有多种实现方式和不同适用场合,不像数组仅采用顺序表方式
集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性即可实现各种复杂操作,大大提高了软件的开发效率-
图
图
-
-
框架图
框架
-
Iterator
-
Map接口
Map 接口 键值对的集合 (双列集合)
├———Hashtable 接口实现类, 同步, 线程安全
├———HashMap 接口实现类 ,没有同步, 线程不安全-
│—————–├ LinkedHashMap 双向链表和哈希表实现
│—————–└ WeakHashMap
├ ——–TreeMap 红黑树对所有的key进行排序
└———IdentifyHashMapMap接口有三个比较重要的实现类,分别是HashMap、TreeMap和HashTable。
TreeMap是有序的,HashMap和HashTable是无序的。
Hashtable的方法是同步的,HashMap的方法不是同步的。这是两者最主要的区别。这就意味着:
Hashtable是线程安全的,HashMap不是线程安全的。
HashMap效率较高,Hashtable效率较低。
如果对同步性或与遗留代码的兼容性没有任何要求,建议使用HashMap。 查看Hashtable的源代码就可以发现,除构造函数外,Hashtable的所有 public 方法声明中都有 synchronized关键字,而HashMap的源码中则没有。
Hashtable不允许null值,HashMap允许null值(key和value都允许)
父类不同:Hashtable的父类是Dictionary,HashMap的父类是AbstractMapMap:
1.Map 不是collection的子接口或者实现类。Map是一个接口。
2.Map 的每个 Entry 都持有两个对象,也就是一个键一个值(key和value),Map 可能会持有相同的值对象但键对象必须是唯一的。- Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
4.Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)
- 2、HashMap
- Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
-
HashMap
(key部分无序,不可重复
key重复,value会被覆盖)
HashMap是很常用的Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null,是非同步的,底层是数组+链表。先对Key用hashcode()计算存放位置,再对Key用equals()遍历链表查找。
默认:初始容量为 16,负载因子为 0.75,当存储元素>容量*负载因子时扩容。
在jdk1.8版本后,java对HashMap做了改进,在链表长度大于8的时候,将后面的数据存在红黑树中,以加快检索速度。
- hashcode和equals
1、如果一个类的equals重写了,那么hashcode必须重写,并且equals方法返回是true,hashcode方法返回的值必须一样。equals方法返回true表示两个对象相同,在同一个单链表上比较。
2、hashcode、equals可以直接用IDEA直接生成,但是两个方法必须同时生成。
- 案例
//测试HashMap集合key部分的元素特点
Map<Integer,String> map =new HashMap<>();
map.put(1111,"zhan1");
map.put(6666,"zhan6");
map.put(7777,"zhan7");
map.put(2222,"zhan2");
map.put(2222,"zhan222");//key重复value会自动覆盖
System.out.println(map.size());
//遍历Map集合
Set<Map.Entry<Integer,String>> set =map.entrySet();
for (Map.Entry<Integer,String> entry:set){
System.out.println(entry.getKey()+"="+entry.getValue());
}
- Hashtable
Hashtable
Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。
初始化容量11;
扩容:原容量*2+1
- 4、Properties
是一个Map集合,继承Hashtable,
Properties的key和value都是String类型
- 案例
//创建一个Properties对象
Properties pro = new Properties();
//掌握存取
pro.setProperty("sd1","ss1");
pro.setProperty("sd2","ss2");
pro.setProperty("sd3","ss3");
pro.setProperty("sd4","ss4");
//获取Value
String u1=pro.getProperty("sd1");
String u2=pro.getProperty("sd2");
String u3=pro.getProperty("sd3");
String u4=pro.getProperty("sd4");
System.out.println(u1);
System.out.println(u2);
System.out.println(u3);
System.out.println(u4);
}
- SortedMap接口
- TreeMap
TreeMap
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步的;
- 其它类
IdentityHashMap和HashMap的具体区别,IdentityHashMap使用 == 判断两个key是否相等,而HashMap使用的是equals方法比较key值。有什么区别呢?
对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等; 如果作用于引用类型的变量,则比较的是所指向的对象的地址。
对于equals方法,注意:equals方法不能作用于基本数据类型的变量
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容
- 方法
方法:
- 案例1
//创建Map集合对象
Map<Integer,String> map =new HashMap<>();
//添加键值对
map.put(1,"zhanf1");//1进行了自动装箱
map.put(2,"zhanf2");
map.put(3,"zhanf3");
map.put(4,"zhanf4");
//通过key获取value
String value=map.get(2);
System.out.println(value);
//获取键值对的数量
System.out.println("键值对的数量:"+map.size());
//通过key删除key-value
map.remove(2);
System.out.println("键值对的数量:"+map.size());
//contains方法底层调用的都是equals进行比对,自定义类型需要重写equals
//判断是否包含某个key
System.out.println(map.containsKey(4));
//判断是否包含某个key
System.out.println(map.containsValue("zhanf3"));
//获取所有的value
Collection<String> values=map.values();
for (String s :values) {
System.out.println(s);
}
//清空集合
map.clear();
System.out.println("键值对的数量:"+map.size());
//判断是否为空
System.out.println(map.isEmpty());
- 案例2
//Map集合遍历
//第一种方式:获取所有的Key,通过key来遍历value
Map<Integer,String> map = new HashMap<>();
map.put(1,"zhanf1");//1进行了自动装箱
map.put(2,"zhanf2");
map.put(3,"zhanf3");
map.put(4,"zhanf4");
//遍历
//获取所有的key,所有的key是一个Set集合
Set<Integer> keys = map.keySet();
//遍历key,通过key获取value
Iterator<Integer> it =keys.iterator();
while (it.hasNext()){
//取出其中一个key
Integer key = it.next();
//通过key,获取value
String value = map.get(key);
System.out.println(key+"="+value);
}
System.out.println("====================");
//foreach也行
for (Integer key: keys) {
System.out.println(key+"="+map.get(key));
}
System.out.println("====================");
//第二种方式:Set<Map.Entry<K,V>> entrySet()
//把map集合直接全部转换成Set集合
//Set集合中元素的类型是:Map.Entry
Set<Map.Entry<Integer,String>> set = map.entrySet();
//迭代
Iterator<Map.Entry<Integer,String>> it2= set.iterator();
while (it2.hasNext()){
Map.Entry<Integer,String> node =it2.next();
Integer key =node.getKey();
String value =node.getValue();
System.out.println(key+"="+value);
}
//foreach
System.out.println("====================");
for (Map.Entry<Integer,String> node : set) {
System.out.println(node.getKey()+"="+node.getValue());
}
- Collection接口
Collection 接口的接口 对象的集合(单列集合)
├——-List 接口:元素按进入先后有序保存,可重复
│—————-├ LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
│—————-├ ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
│—————-└ Vector 接口实现类 数组, 同步, 线程安全
│ ———————-└ Stack 是Vector类的实现类
└——-Set 接口: 仅接收一次,不可重复,并做内部排序
├—————-└HashSet 使用hash表(数组)存储元素
│————————└ LinkedHashSet 链表维护元素的插入次序
└ —————-TreeSet 底层实现为二叉树,元素排好序
- 接口SortedSet
- Set
—Set 无序,唯一
HashSet
底层数据结构是哈希表。(无序,唯一)
如何来保证元素唯一性?
1.依赖两个方法:hashCode()和equals()
LinkedHashSet
底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
1.由链表保证元素有序
2.由哈希表保证元素唯一
TreeSet
底层数据结构是红黑树。(唯一,有序)
1. 如何保证元素排序的呢?
自然排序
比较器排序
2.如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定
Set:
1.不允许重复对象
2. 无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。
3. 只允许一个 null 元素
Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。
- 3、HashSet
底层数据结构采用哈希表实现,
元素无序且唯一,
线程不安全,效率高,可以存储null元素,元素的唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的,如果没有重写这两个方法,则无法保证元素的唯一性。
- 要点
Set<String> strs = new HashSet<>();
//添加元素
strs.add("string 1");
strs.add("string 2");
strs.add("string 3");
strs.add("string 1");
//无序,不可重复
for (String s :strs) {
System.out.println(s);
}
- LinkedHashSet
底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。线程不安全,效率高。
- 5、TreeSet
底层数据结构采用二叉树TreeMap来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode和equals()方法,
二叉树结构保证了元素的有序性。根据构造方法不同,分为自然排序(无参构造)和比较器排序(有参构造),
自然排序要求元素必须实现Compareable接口,并重写里面的compareTo()方法,元素通过比较返回的int值来判断排序序列,返回0说明两个对象相同,不需要存储;
比较器排需要在TreeSet初始化是时候传入一个实现Comparator接口的比较器对象,或者采用匿名内部类的方式new一个Comparator对象,重写里面的compare()方法;
- 要点
//创建集合对象
Set<String> strs = new TreeSet<>();
//添加元素
strs.add("1");
strs.add("5");
strs.add("4");
strs.add("3");
strs.add("2");
//遍历
//TreeSet可以自动排序
for (String s: strs) {
System.out.println(s);
}
- comparabl和comparator
当比较股则不会发生改变时,或者规则只有一个时选择Comparable
如果比较规则有多个,并且规则需要频繁切换时,使用Com[arator
- List接口
— List 有序,可重复
ArrayList
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程不安全,效率高
Vector
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程安全,效率低
LinkedList
优点: 底层数据结构是链表,查询慢,增删快。
缺点: 线程不安全,效率高
List:
1.可以允许重复的对象。
2.可以插入多个null元素。
3.是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
常用的实现类有 ArrayList、LinkedList 和 Vector。
ArrayList和Vector底层都是用数组实现的,最大的区 别在于Vector是线程安全的,因此效率不如ArrayList。
LinkedList底层用双链表实现,与前两个相比,优点是便于增删元素,缺点是访问元素性能效率相对低。
(数组连续内存空间,查找速度快,增删慢;链表充分利用了内存,存储空间是不连续的,首尾存储上下一个节点的信息,所以寻址麻烦,查找速度慢,但是增删快。)
还有一点是继承Vector类的Stack,Java自带的栈。进栈push(),弹栈pop(),获取栈顶peek(),判断空否empty()...
- 1、ArrayList
ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
- 要点
1、初始化容量是10
2、底层是Object类型的数组
3、构造方法
new ArratList()
new ArrayList(10)
Collection c= new HashSet();
List li=new ArrayList(c);//将HashSet转为List集合
4、ArrayList集合的扩容,原容量的1.5倍
(尽量减少扩容)
- LinkedList
LinkedList 底层数据结构是 双向链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
- Vector
Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
- 要点
1、初始化容量是10
2、底层是Object类型的数组
3、构造方法
new Vector()
new Vector(10)
4、Vector集合的扩容,原容量的2倍
(尽量减少扩容)
- 特有,常用的方法
//创建List类型集合
List myList = new ArrayList();
//添加元素
myList.add("A");//默认在尾部添加元素(效率较高)
myList.add("B");
myList.add("C");
myList.add("D");
myList.add(1,"kkkk");//指定位置添加(效率较低)
//迭代
Iterator it = myList.iterator();
while (it.hasNext()){
Object obj=it.next();
System.out.println(obj);
}
System.out.println();
//根据下标获取元素
Object obj2=myList.get(0);
System.out.println(obj2);
System.out.println();
//因为有下标,list可以通过下表遍历方式
//通过下标遍历
for (int i = 0; i <myList.size() ; i++) {
obj2=myList.get(i);
System.out.println(obj2);
}
System.out.println();
//获取指定对象第一次出现的索引
System.out.println(myList.indexOf("kkkk"));
//获取指定对象最后一次出现的索引
System.out.println(myList.indexOf("C"));
System.out.println();
//删除指定下标的元素
myList.remove(1);
//修改指定位置的元素
myList.set(0,"AA");
for (int i = 0; i <myList.size() ; i++) {
obj2=myList.get(i);
System.out.println(obj2);
}
System.out.println();
- 方法
方法:
- 所有方法实现案例
Collection c =new ArrayList();
//add添加元素
c.add(12000);//自动装箱,实际是放入了一个对象Integer
c.add(3.14);
//size获取集合中得元素个数
System.out.println(c.size());
//clear清空集合
c.clear();
System.out.println(c.size());
c.add("hello");//"hello"对象得地址放到集合中
c.add("word");
//contains 判断当前集合中是否包含此元素
System.out.println(c.contains("hello"));
//remove删除集合中某个元素
System.out.println(c.size());
c.remove("hello");
System.out.println(c.size());
//isEmpty判断集合是否为空
System.out.println(c.isEmpty());
c.clear();
System.out.println(c.isEmpty());
//转换成数组toArray
c.add("word1");
c.add("word2");
c.add("word3");
c.add("word4");
Object[] obj = c.toArray();
for (int i = 0; i <obj.length ; i++) {
System.out.println(obj[i]);
}
}
- 补充
contains方法:存放在集合中的类型(类似自定义的对象),一定要重写equals方法(在自定义的对象类里)
remove
- 集合遍历 / 迭代Interator
* 以下两个方法是迭代器对象Interator中的方法:
* boolean hasNext( )如果仍有元素可以迭代,则返回true
* Object next()返回迭代的下一个元素
- 案例1
//collection通用的(子类也通用)遍历方式/迭代方式
//在map中不能通用
Collection c= new ArrayList();
c.add("abc");
c.add("def");
c.add(100);
c.add(new Object());
//对集合Collection进行遍历/迭代
//第一步 :获取集合对象的迭代器对象Iterator
Iterator it = c.iterator();//迭代器对象it:负责遍历/迭代集合当中元素的;一开始没有指向第一个元素,需要使用next
//第二步:通过以上获取的迭代器对象开始迭代/遍历集合
/**
* 以下两个方法是迭代器对象Interator中的方法:
* boolean hasNext( )如果仍有元素可以迭代,则返回true
* Object next()返回迭代的下一个元素
*/
/* boolean hasNext= it.hasNext();
if (hasNext){
Object obj = it.next();
System.out.println(obj);
}*/
while(it.hasNext()){
Object obj=it.next();
System.out.println(obj);
}
}
- 案例2
//创建集合对象
Collection c = new ArrayList();
//添加元素
c.add(1);
c.add(2);
c.add(3);
c.add(4);
//迭代集合
Iterator it = c.iterator();
while ( it.hasNext()){
System.out.println(it.next());
}
System.out.println();
//HashSet集合:无序不可重复
//无序:存时的顺序与,取时不同
Collection c2 = new HashSet();
c2.add(1);
c2.add(1);
c2.add(7);
c2.add(1);
c2.add(6);
c2.add(1);
c2.add(3);
Iterator it2 =c2.iterator();
while(it2.hasNext()){
System.out.println(it2.next());
}
}
- 重点
- 集合结构只要发生改变,迭代器必须重新获取
- remove方法
- 不能在迭代过程中通过集合来删除集合中的元素,但可以通过迭代器来删除(删除的是快照)
工具类Collections
- 子主题 1
- 子主题 2
泛型机制
-
格式
List myList = new ArrayList( );
Iterator it=Animal.iterator( ) ;
-
只在编译阶段起作用,给编译器参考,运行阶段没有用
-
好处:集合中存储的元素同一;从集合中取出的元素类型是泛型指定 的类型,不需要大量的“向下转型”
-
缺点:集合中的元素缺乏多样性
-
自动类型推断机制(砖石表达式)
List myList = new ArrayList<>( );
ArrayList<这里的类型会自动推断>( )
Iterator it=Animal.iterator( ) ;
-
自定义泛型
public class Name<标识符>{
public 标识符 sum(){
}
…
}
foreach
-
语法
for(元素类型 变量名 : 数组或集合){
System.out.println(变量名);
}for (int date:array) {
System.out.println(date);
} -
没有下标的迭代集合
IO流
-
四大分类
I:Input
O: Output
通过IO可以完成对文件的读写。分类:
一种方式是按照流的方向进行分类:
以内存作为参照物,
往内存中去,叫输入(Input) 或者叫读(Reader)
从内存中出来,叫输出( Ouput) 或者叫写(Writer)另一种方式是按照读取数据方式不同进行分类:
有的流是按照字节的方式读取数据,一次读取一个字节。这种流是万能的,什么类型的文件都可以读取有的流是按照字符的方式读数据的,一次读取一个字符,为了方便读取普通文本而存在的。
所有的流都在java.io.*下
-
java.io.InputStream字节输入流
注意结尾,什么结尾就是什么流
-
java.io.OutputStream字节输出流
-
java.io.Reader字符输入流
-
java.io.Writer字符输出流
-
-
需要掌握的16个流
文件专属:
java.io.FileInputStream(掌握)
java.io.FileOutputStream(掌握)
java.io.FileReader
java.io.FileWriter转换流:
java.io.InputStreamReader
java.io.OutputStreamWriter缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream数据流专属:
java.io.DataInputStream
java.io.DataOutputStream标准输出流:
java.io.PrintWriter
java.io.PrintStream(掌握)对象专属流:
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)-
FileInputStream
- 案例1
-
//创建文件字节输入流对象
FileInputStream fis=null;
try {
//D:/Folders.2/Java也可以
fis = new FileInputStream(“D:\Folders.2\Java\temp.txt”);
//开始读
int readData =fis.read();//这个方法返回的是:读到的“字节”本身
System.out.println(readData);
readData =fis.read();
System.out.println(readData);
}catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//在finally中确保流一定关闭
if (fis != null) {
//关闭流的前提是:流不是空
try{
fis.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
- 案例2
public static void main(String[] args) {
/*
int resd (byte[] b)
一次读取b.length个字节
提高交换效率
*/
FileInputStream fis=null;
try {
//IDEA默认的当前路径是:工程Project的根位置作为起点(与src文件同一文件下)
fis=new FileInputStream("temp.txt");
byte[] bytes = new byte[4];
//这个方法返回值:读取到的字节数量
int readCount =fis.read(bytes);
System.out.println(readCount);
//System.out.println(new String(bytes));
System.out.println(new String(bytes,0,readCount));
readCount =fis.read(bytes);//第二次读取会将原来的内容覆盖一部分,只读取了两个字节(假设原先有abcdef)
System.out.println(readCount);
//System.out.println(new String(bytes));
System.out.println(new String(bytes,0,readCount));
readCount =fis.read(bytes);//1个字节没读取到,返回-1
System.out.println(readCount);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 案例3(改进)
public static void main(String[] args) {
FileInputStream fis =null;
try {
fis=new FileInputStream("temp.txt");
//准备一个byte数组
byte[] bytes=new byte[4];
/* while (true){
int readCount =fis.read(bytes);
if (readCount==-1){
break;
}
//把byte数组转化成字符串,读到多少个转多少个
System.out.println(new String(bytes,0,readCount));
}*/
//改进
int readCount =0;
while ((readCount=fis.read(bytes))!=-1){
System.out.print(new String(bytes,0,readCount));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 部分常用方法
- FileOutputStream
- 子主题 1
- 子主题 2
- FileReaderText
- 案例
public static void main(String[] args) {
/*
文件输入流,只能读取普通文本
读取文本内容时,比较方便,快捷
*/
FileReader reader=null;
try {
//创建文件输入流
reader=new FileReader("myfile");
//使用foreach对数组进行遍历也可以实现
//开始读
char[] chars=new char[4];
int readCount=0;
while ((readCount=reader.read(chars))!=-1){
System.out.print(new String(chars,0,readCount));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- FileWriterText
- 案例
public static void main(String[] args) {
/*
文件字符输出流,写
只能输出普通文本
*/
FileWriter out=null;
try {
out = new FileWriter("file",true);//会清空原文件,不清空加true
//开始写
char[]chars={'字','符','串'};
out.write(chars);
out.write(chars,0,2);
//加换行
out.write("\n");
out.write("java输出流");
//刷新流
out.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 带缓冲区的流
- BuffereReader
- 案例1
public static void main(String[] args) throws Exception{
/*
带有缓冲区的字符输入流
不需要自定义char数组,自带缓冲
*/
FileReader reader=new FileReader("src//io//Copy02.java");
/*
当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流
外部负责包装的这个流,叫做:包装流(处理流)
当前程序:
FileReader为节点流
BuffereReader为包装流(处理流)
*/
BufferedReader br=new BufferedReader(reader);
/* //读一行
String filess= br.readLine();
System.out.println(filess);*/
//readLine读取一行但不带换行符
String s= null;
while ((s=br.readLine())!=null){
System.out.println(s);
}
//关闭流;包装流只需关闭外部流,节点流自动关闭
br.close();
}
- 案例2(使用到字符转换流)
public static void main(String[] args) throws Exception{
/* //字节流
FileInputStream in =new FileInputStream("src//io//Copy02.java");
//通过转换流转换
//in是节点流,inputStreamReader是包装流
InputStreamReader inputStreamReader =new InputStreamReader(in);
//只能接收字符流
//inputStreamReader是节点流,br是包装流
BufferedReader br=new BufferedReader(inputStreamReader);*/
//合并
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("src//io//Copy02.java")));
String line=null;
while ((line=br.readLine())!=null){
System.out.println(line);
}
//关闭最外层流
br.close();
}
- BuffereWriter
- 子主题 1
- 子主题 3
- 子主题 4
- 数据流
- DataInputStream
- DataOutputStream
- 标准输出流
- PrintWriter
- PrintStream
- 案例
public PrintStreamText(FileOutputStream fileOutputStream) {
}
public static void main(String[] args) throws Exception{
/*
标准的字节输出流,默认输出到控制台
*/
System.out.println("hello world");
//分开写
PrintStream ps=System.out;
ps.println("sss");
/*Scanner s=new Scanner(System.in);
int ss=s.nextInt();*/
//改变指向,指向log文件
PrintStream printStream =new PrintStream(new FileOutputStream("log"));
//修改输出方向
System.setOut(printStream);
//再输出
System.out.println("hello world");
//标准输出流不需要关闭
}
- 实例
public class Logger {
public static void log(String msg) {
/*
日志工具
*/
try {
//指向一个文件
PrintStream out = new PrintStream(new FileOutputStream("log.txt",true));
//改变输出方向
System.setOut(out);
//日期当前时间
Date nowTime =new Date();
SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime=sdf.format(nowTime);
System.out.println(strTime+":"+msg);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
public class LogText {
public static void main(String[] args) {
//测试工具类
Logger.log("调用了A");
Logger.log("调用了B");
Logger.log("调用了C");
}
}
- 对象专属流
- .ObjectOutputStream
- 序列化
拆分对象
序列化:Serialize Java对象存储到文件中,将Java对象的状态保存下来的过程
- 案例
序列化多个对象使用数组( List 集合)
序列化的对象必须实现Implements接口
/*
1、java.io.NotSerializableException: bean.Student
Student不支持序列化
2、参与序列化和反序列化的对象,必须实现Serializable接口:
public interface Serializable{
}
起到标识的作用:生成序列化版本号
3、序列化版本号:
*/
public class ObjectOutputStreamText01 {
public static void main(String[] args) throws Exception{
Student s=new Student(1111,"zhangsna");
//序列化
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("students"));
//序列化对象
oos.writeObject(s);
//刷新
oos.flush();
//关闭
oos.close();
}
}
public class Student implements Serializable {
//快捷键:选中类名Alt+回车
private static final long serialVersionUID = 5445665688L;
private int no;
private String name;
public Student() {
}
public Student(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public String getName() {
return name;
}
public void setNo(int no) {
this.no = no;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
- ObjectInputStream
- 反序列化
组装对象
反序列化:DeSerialize 将硬盘上的数据重新恢复到内存中,恢复Java对象
- 案例
public class ObjectInputStreamText01 {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
//开始反序列化:读
Object obj = ois.readObject();
//反序列化回来是一个学生对象,所以会调用学生对象的toString方法
System.out.println(obj );
ois.close();
}
}
- 关键字transient表示游离,不参与序列化
列:private transient String name;
- 建议:将序列化版本号手动写出来。即使类修改了,也可以识别 。列:private static final long serialVersionUID = 5445665688L;
Java虚拟机判别一个类是否相同:一、类名
二、序列化版本号
-
java.io.File类
不能完成文件的读写,不属于4种流
是文件和目录路径名的抽象表示形式。
一个File对象可能是一个目录,或者文件
列:D:\Folders.2\Java\IO-
常用方法
-
案例1
public static void main(String[] args) throws Exception{
//创建File对象
File f1 =new File(“fileText01”);//判断是否存在 System.out.println(f1.exists());
/* //如果fileText01不存在,则以文件的形式创建出来
if(!f1.exists()){
f1.createNewFile();
}*//* //如果fileText01不存在,以目录的形式创建出来
if(!f1.exists()){
//以目录的形式新建
f1.mkdir();
}*//* //以多重目录新建
File f2 =new File(“a/b/c/d”);
if(!f1.exists()){
//以目录的形式新建
f2.mkdir();
}*/File f3 =new File("a/b/c/d"); //获取文件的父路径 String parentPath=f3.getParent(); System.out.println(parentPath); File parentFile =f3.getParentFile(); System.out.println("获取绝对路径:"+parentFile.getAbsolutePath()); }
-
案例2
public static void main(String[] args) {
File f1=new File(“fileText01”);
//获取文件名
System.out.println(“文件名”+f1.getName());//判断是否是一个目录 System.out.println(f1.isDirectory()); //判断是否是一个文件 System.out.println(f1.isFile()); //获取文件大小 System.out.println(f1.length()); //获取文件最后一次修改时间 long haomiao =f1.lastModified();//这个毫秒是从1970年到现在的总毫秒数
}
-
案例3
-
-
public static void main(String[] args) {
//File[]listFiles
//获取当前目录下的所有子文件
File f =new File(“D:\Folders.2”);
File[] files=f.listFiles();
//foreach
for (File file :files) {
System.out.println(file.getAbsolutePath());
}
}
- 目录复制
-
IO+Properities联合应用(反射机制中有所应用)
- 案例
/*
Io +Properties
/
public class IoPropertiesText01 {
public static void main(String[] args) throws Exception{
/
properties是一个Map集合,Properties的key和value都是String类型
将file01文件中的数加载到Properties对象中
经常改变的数据,可以单独写到一个文件中,使用程序动态读取。
只需要修改文件的内容,Java代码不用修改,不需要重新编译,服务器也不需要重启,就可以得到动态信息。
配置文件内容格式:
key1=value1
key1=value1
这种文件称为属性配置文件,建议以.properties结尾
Properties是专门存放属性配置内容的一个类
*/
//新建一个输入流对象
FileReader reader=new FileReader("file01.properties");
//新建一个Map集合
Properties pro =new Properties();
//调用Properties的load方法将文件中的数据加载到Map集合中
pro.load(reader);//文件等号=左边做key,右边做value
//通过key获取value
String username=pro.getProperty("username");
System.out.println(username);
String password=pro.getProperty("password");
System.out.println(password);
}
}
线程与进程
-
概叙
进程:是一个应用程序(一个进程是一个软件)
线程:是一个进程中的执行场景/执行单元一个进程可以启动多个线程
-
进程与线程关系
进程可以看作公司
线程可以看作公司员工进程A和进程B的内存独立不共享
线程A和线程B:
在Java中线程A、B堆内存和方法区内存共享,但栈内存独立,一个线程一个栈 -
实现方法
-
第一种方法:编写一个类,直接继承Thread,重写run方法
- 案例
-
public class ThreadText01 {
public static void main(String[] args) {
//这里是main方法,这里代码属于主线程,在主栈中运行
//新建一个分支线程对象
MyTread myTread=new MyTread();
//启动线程
/*
start( )方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成后,瞬间就结束了。
任务:只要新的栈空间开辟出来,start方法就结束了,线程就启动成功了
启动成功会自动调用run方法,
run方法在分支栈的栈低,main方法在主栈底部,run 和main方法是平级的
*/
myTread.start();
//这里的代码话说运行在主线程中。
for (int i = 0; i <10 ; i++) {
System.out.println("主线程: "+i);
}
}
}
class MyTread extends Thread{
@Override
public void run() {
//编写程序,这段程序运行在分支线程中(分支栈)
for (int i = 0; i <10 ; i++) {
System.out.println("分支线程: "+i);
}
}
}
- 第二中方法:编写一个类,实现Runnable接口,实现run方法(较为常用)
- 案例
package thread;
public class ThreadText02 {
public static void main(String[] args) {
/* //创建一个可运行的对象
MyRunnable myRunnable=new MyRunnable();
//将可运行对象封装成一个线程对象
Thread t=new Thread(myRunnable);*/
//合并
Thread t=new Thread(new MyRunnable());
//启动线程
t.start();
for (int i = 0; i <10 ; i++) {
System.out.println("主线程: "+i);
}
}
}
//这是一个线程类,是一个可运行的类,还不是一个线程
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("分支线程: "+i);
}
}
}
-
线程生命周期之阻塞状态
当一个线程遇到阻塞事件,列如接收到用户键盘输入,或者sleep等方法,此时线程会进入阻塞状态,阻塞状态的线程会抛弃之前占有的CPU时间片
接触后回到就绪状态
五个状态:新键状态、就绪状态、运行状态、死亡状态、阻塞状态(发生在就绪状态和运行状态之间)
-
线程的一些东西
- 获取线程的一些东西:列入名字,对象
package thread;
public class ThreadText03 {
public static void main(String[] args) {
/*
获取当前线程对象
获取线程对象名字
修改线程对象的名字
*/
//current就是当前线程对象
//这个代码出现子啊main方法中,所以当前线程就是主线程
Thread current= Thread.currentThread();
System.out.println(current.getName());
//创建线程对象
MyThread2 myThread2=new MyThread2();
//设置线程对象名字
myThread2.setName("tttt");
//获取线程的名字
String s=myThread2.getName();//默认Thread-0
System.out.println("线程的名字:"+s);
//启动线程
myThread2.start();
}
}
class MyThread2 extends Thread{
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("分支线程: "+i);
}
}
}
当前对象不是线程对象时,this和super就饿不饿用了
Thread.currentThread();
-
sleep方法
static void sleep(long millis)
作用:让当线程进入休眠,”阻塞状态“,放弃占有的CPU时间片,让给其它线程使用
出现在那个线程中就让那个线程休眠
可以实现让一个程序一段时间执行一次
-
案例
package thread;
-
public class ThreadText04 {
public static void main(String[] args) {
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(111111);
}
}
- 面试题
package thread;
public class ThreadText05 {
public static void main(String[] args) {
Thread t=new MyTread3();
t.setName("t");
t.start();
//调用sleep
try {
//问题:这行代码会进入休眠状态吗?
t.sleep(1000*5);//在执行的时候会转换成:Thread.sleep(1000*5);
//这行代码的作用是:让当前线程进入休眠,main进入休眠
//这行代码出现在main方法中,main线程进入休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----------");
}
}
class MyTread3 extends Thread{
public void run(){
for (int i = 0; i <10000 ; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
- 终止睡眠(干扰)
终端线程的睡眠
t.interrupt( );//干扰(解除睡眠)
//这种终断睡眠机制依靠了Java中的异常处理机制
- 强行终止线程
t.stop( );
不建议使用,已过时
- 合理的终止一个线程
package thread;
public class ThreadText06 {
public static void main(String[] args) {
MyRunnable3 r=new MyRunnable3();
Thread t=new Thread(r);
t.setName("t");
t.start();
//模拟5秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//终止线程
//想终止的时候改为false即可
r.run=false;
}
}
class MyRunnable3 implements Runnable{
//打一个布尔标记
boolean run=true;
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if(run){
System.out.println(Thread.currentThread().getName()+"--->"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
//在return结束前可以将需要保存的写在这里
//
return;
}
}
}
}
-
线程安全
-
什么时候出现线程安全问题
条件1:多线程并发
条件2:有共享数据
条件3:共享数据有修改行为满足以上三个条件之后,就会存在线程安全问题。
-
那些变量会有线程安全问题
局部变量永远不会出现线程安全问题。因为局部变量不共享,一个线程一个栈。局部变量在栈中,
实列变量在堆中,堆只有一个。
静态变量在方法区中方法区只有一个。
堆和方法区都是多线程共享的,所以可能存在线安全问题 -
synchronized出现在实例方法上一定锁的是this:public void synchronized withdraw(double money){
public void withdraw(double money){
-
三中使用方式
第一种:同步代码块
灵活
synchronized(线程共享对象){
同步代码块;
}第二种:在实例方法上使用synchronized
表示共享对象一定是this
并且同步代码块是整个方法第三种:在静态方法上使用synchronized
表示类锁
类锁永远只有一把(无论对象的多少)-
死锁
死锁很难调试
出现死锁程序不包异常synchronized最好不要嵌套使用容易产生死锁现象
- 案例
-
-
-
package thread.Deadlock;
/*
死锁很难调试
*/
public class DeadLock {
public static void main(String[] args) {
Object o1=new Object();
Object o2=new Object();
//t1和t2两个线程共享o1,o2
Thread t1=new MyThread(o1,o2);
Thread t2=new MyThread2(o1,o2);
t1.start();
t2.start();
}
}
class MyThread extends Thread{
Object o1;
Object o2;
public MyThread(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
public void run(){
synchronized (o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
}
}
}
}
class MyThread2 extends Thread{
Object o1;
Object o2;
public MyThread2(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
public void run(){
synchronized (o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
}
}
}
}
- 怎么解决线程安全问题
线程排队执行。(不能并发)
用排队执行解决线程安全问题
这种机制称为线程同步机制
(线程同步)
异步编程模型:并发
同步编程模型:排队
- 在以后开发中怎么解决线程安全问题
不能一开始就使用synchronized,会使系统用户吞吐量降低,用户体验差
方法:
第一种:尽量使用局部变量替代“实例变量和静态变量”
第二种:如果必须是实例变量,那么可以考虑创建多个对象,这样实例对象就的内存就不共享了(一个线程对应1个对象,对象不共享,就没有数据安全问题了)
第三种:如果不能使用局部变量,对象也不能创建多个,这个时候就只能选择synchronized线程同步机制
- 案例
- 不使用线程同步机制,出现线程安全问题
- 类1
package thread.Threadsafe;
/*
银行账户
不使用线程同步机制,多线程对同一个账户进行取款,出现线程安全问题
*/
public class Account {
//账号
private String actno;
//余额
private double balance;
public Account() {
}
public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public double getBalance() {
return balance;
}
public void setActno(String actno) {
this.actno = actno;
}
public void setBalance(double balance) {
this.balance = balance;
}
//取款方法
public void withdraw(double money){
//取款前的余额
double brfore =this.getBalance();
//取款后的余额
double after = brfore-money;
//模拟延迟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新余额
this.setBalance(after);
}
}
- 类2
package thread.Threadsafe;
public class AccountThread extends Thread{
//两个线程必须共享同一个账户对象
private Account act;
//通过构造方法传递过来账户对象
public AccountThread(Account act){
this.act=act;
}
public void run(){//run方法在start时自动执行
//run方法执行表示取款操作
//假设取款5000
double money=5000;
//取款
act.withdraw(money);
System.out.println(Thread.currentThread().getName()+"对: "+act.getActno()+"取款成功,余额:"+act.getBalance());
}
}
- 类2
package thread.Threadsafe;
public class Text {
public static void main(String[] args){
//创建账户对象(只创建一个)
Account act = new Account("axt-001",10000);
//创建两个线程
Thread t1 =new AccountThread(act);
Thread t2 =new AccountThread(act);
//设置线程name
t1.setName("t1");
t2.setName("t2");
//启动线程
t1.start();
t2.start();
}
}
- 使用线程同步机制,解决现线程安全问题
- 类1
package thread.Threadsafe2;
import thread.Threadsafe2.Account;
import thread.Threadsafe2.AccountThread;
public class Text {
public static void main(String[] args){
//创建账户对象(只创建一个)
Account act = new Account("axt-001",10000);
//创建两个线程
Thread t1 =new thread.Threadsafe2.AccountThread(act);
Thread t2 =new AccountThread(act);
//设置线程name
t1.setName("t1");
t2.setName("t2");
//启动线程
t1.start();
t2.start();
}
}
- 类2
package thread.Threadsafe2;
public class AccountThread extends Thread{
//两个线程必须共享同一个账户对象
private thread.Threadsafe2.Account act;
//通过构造方法传递过来账户对象
public AccountThread(Account act){
this.act=act;
}
public void run(){//run方法在start时自动执行
//run方法执行表示取款操作
//假设取款5000
double money=5000;
//取款
act.withdraw(money);
System.out.println(Thread.currentThread().getName()+"对: "+act.getActno()+"取款成功,余额:"+act.getBalance());
}
}
- 类3
package thread.Threadsafe2;
import thread.Threadsafe2.Account;
import thread.Threadsafe2.AccountThread;
public class Text {
public static void main(String[] args){
//创建账户对象(只创建一个)
Account act = new Account("axt-001",10000);
//创建两个线程
Thread t1 =new thread.Threadsafe2.AccountThread(act);
Thread t2 =new AccountThread(act);
//设置线程name
t1.setName("t1");
t2.setName("t2");
//启动线程
t1.start();
t2.start();
}
}
- 守护线程
Java种线程分为两打类:
一类是:用户线程
一类是:守护线程(后台线程)
其中代表性的是:垃圾回收线程(守护线程)
守护线程特点:
一般是死循环,所有用户线程只要结束,守护线程自动结束
主线程是一个用户线程
在所有线程结束时备份时可以用到
- 实现案例
package thread;
/*
守护线程
*/
public class ThreadText07 {
public static void main(String[] args) {
Thread t=new BakDataThread();
t.setName("备份数据线程");
//启动之前将线程设置为守护线程
t.setDaemon(true);
t.start();
//主线程:是用户线程
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class BakDataThread extends Thread{
public void run(){
int i=0;
while (true){
System.out.println(Thread.currentThread().getName()+"-->"+(++i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 定时器
作用:间隔特定的时间,执行特定的程序
利用sleep是最原始的定时器
在Java中已经写好了(但是也不常用,很多框架都支持定时任务):java.util.Time,可以直接使用
多数是Spring框架下的定时器
- 案例
package thread;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TimerText {
public static void main(String[] args) throws Exception{
//创建定时器对象
Timer timer= new Timer();
//Timer timer= new Timer(true);//守护线程的方式
//指定定时任务
//timer.schedule(定时任务,第一次执行时间,间隔多久执行一次);
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date firtTime= sdf.parse("2021-3-06 15:51:00");
timer.schedule(new LogTimerTask(),firtTime,1000*10);
}
}
//编写一个定时任务类
//假设这是一个记录日志的定时任务
class LogTimerTask extends TimerTask {
@Override
public void run() {
//编写执行的任务
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String stime= sdf.format(new Date());
System.out.println(stime+"完成一次数据备份");
}
}
- 实现线程的第三种方式,FutureTask方式,实现Callable接口
这种方式可以获取线程的返回值
前两种不行
效率较低
- 案例
package thread;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/*
实现线程的第三种方式,FutureTask方式,实现Callable接口
*/
public class ThreadText08 {
public static void main(String[] args) throws Exception{
//第一步:创建一个”未来任务类“对象
//参数很重要,需要一个Callable()接口实现对象
FutureTask task = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {//Callable()方法相当于run方法,不过这个有返回值
//这里执行的任务可能回有一个执行结果
System.out.println("begin");
Thread.sleep(1000*5);
System.out.println("end");
int i=10000;
return i;//自动装箱变为Intger
}
});
//创建线程对象
Thread t = new Thread(task);
//启动线程
t.start();
//在主线程种怎么获得另一个线程的执行结果
Object obj = task.get();//这里会等待get方法结束,导致当前线程阻塞
System.out.println("ssssssssssssssss");
}
}
- 关于Object类中的wait和notify方法(生产者和消费者)
建立在synchronized的基础上
第一:wait和notify方法不是线程方法,是Java种任何一个类自带的,是Objet类种自带的。
wait方法作用:
Object o = new Object();
o.wait();
表示:
让正在o对象上活动的线程进入等待状态,直到被唤醒
会让当前线程(正在o对象上活动的线程)进入等待状态。
并且会释放之前占有的o对象的锁
notify方法作用:
o.notfy( );的调用可以使正在o对象上活动的线程重新唤醒;
只会通知不会释放锁
notifyAll( );唤醒所有在正在o对象上活动的线程
- 实现生产者和消费者模式
生产线程负责生产
消费线程负责消费
生产线程和消费线程要达到均衡
是一种特殊的业务需求
- 案例
package thread;
import java.util.*;
/*
关于Object类中的wait和notify方法 实现生产者和消费者模式
*/
public class ThreadText09 {
public static void main(String[] args) {
/*
模拟:仓库采用List集合
List集合中假设只能存储一个元素
一个元素就表示仓库满了
必须做到:生产1个消费个
*/
//创建一个对象,共享的
List list= new ArrayList();
//创建两个线程对象
//生产者线程
Thread t1= new Thread(new Producer(list));
//消费者线程
Thread t2= new Thread(new Consumer(list));
t1.setName("生产者线程");
t2.setName("消费者线程");
t1.start();
t2.start();
}
}
//生产线程
class Producer implements Runnable{
//仓库
private List list;
public Producer(List list) {
this.list = list;
}
@Override
public void run() {
//一直生产
//使用while模拟
while (true){
//给仓库对象List加锁
synchronized (list){
if (list.size()>0){
//当前线程进入等待状态
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//程序到这里,说明仓库是空的,可以生产
Object obj = new Object();
list.add(obj);
System.out.println(Thread.currentThread().getName()+"--->"+obj);
//唤醒消费者消费
list.notify();
}
}
}
}
//消费线程
class Consumer implements Runnable{
//仓库
private List list;
public Consumer(List list) {
this.list = list;
}
@Override
public void run() {
//一直消费
while (true){
synchronized (list){
if (list.size()==0){
//仓库已经空了
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//执行到此处,说明仓库中有数据,进行消费
Object obj = list.remove(0);
System.out.println(Thread.currentThread().getName()+"--->"+obj);
//唤醒生产者生产
list.notify();//唤醒所有也没关系
}
}
}
}
反射机制
java.lang.reflect.*
-
作用
可以操控字节码文件(读修改)
-
重要的类
-
java.lang.Class 代表整个字节码,代表一个类型
-
获取Class的三种方式
第一种:Class c = Class.forName(“完整类名带包名”)
第二种:Class c = 对象.getClass( );
第三种:Java中任何一种类型都有 .class属性
- 案例
-
-
//获取Class
public class ReflectTest01 {
public static void main(String[] args) {
/*
Class.forName( )
静态方法
方法参数是一个字符串
字符串需要的是一个完整类名
完整类名必须带有包名
*/
Class c1=null;
try {
c1 = Class.forName(“java.lang.String”);//c1代表String.class文件,或者String类型
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
/*
java中任何一个对象都有一个方法getClass( )
*/
String s="abc";
Class x = s.getClass();//x代表String.class字节码文件,或者String类型
System.out.println(c1==x);//true(==判断的是对象内存地址)
/*
Java中任何一种类型都有 .class属性
*/
Class z=String.class;//z代表String.class字节码文件,或者String类型
System.out.println(z==x);//true(==判断的是对象内存地址)
}
}
- Class.forName
如果只希望一个类的静态代码块执行,其它代码不执行
可以使用Class.forName
这个方法的执行会导致类加载,类加载时,静态代码块执行
- 作用
验证反射机制的灵活性
修改配置文件,可以实例化不同类型的变量
- 案例
import java.io.FileReader;
import java.util.Properties;
public class ReflectText03 {
/*
验证反射机制的灵活性
修改配置文件,可以实例化不同类型的变量
*/
public static void main(String[] args) throws Exception{
//通过IO流读取classinfo.properties文件
FileReader reader = new FileReader("src\\classinfo.properties");
//创建属性类对象pro
Properties pro = new Properties();
//加载
pro.load(reader);
//关闭流
reader.close();
//通过key 获取value
String className=pro.getProperty("className");
//System.out.println(className);
//通过反射机制实例化对象
Class c = Class.forName(className);
Object obj = c.newInstance();
System.out.println(obj);
}
}
- java.lang .reflect.Method 代表字节码中的方法字节码
- java.lang.reflect.Constructor 代表机制码中的构造方法字节码
- java.lang.reflect.Field 代表字节码中的属性字节码
-
获取类路径下文件的绝对路径
- 案例
import java.io.FileReader;
public class AboutPath {
/*
获取类路径下文件的绝对路径
*/
public static void main(String[] args) throws Exception{
//这种路径可移植性差
//FileReader reader = new FileReader("src\\classinfo.properties");
//比较通用的路径,即使代码换位置了,任可以用
//前提文件必须在类路径下
//类路径:在src下的都是类路径
String path =Thread.currentThread().getContextClassLoader().getResource("classinfo.properties").getPath();
System.out.println(path);//拿到绝对路径
String path2 =Thread.currentThread().getContextClassLoader().getResource("bean/fileTest01.properties").getPath();
System.out.println(path2);//拿到绝对路径
}
}
- 改进
import java.io.FileReader;
import java.io.InputStream;
import java.util.Properties;
public class IoPropertiesTest {
public static void main(String[] args) throws Exception{
/* String path =Thread.currentThread().getContextClassLoader().getResource("bean/fileTest01.properties").getPath();
FileReader reader = new FileReader(path);*/
//改进:直接以流的形式返回
InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("bean/fileTest01.properties");
Properties pro = new Properties();
pro.load(reader);
reader.close();
//通过key获取value
String className=pro.getProperty("className");
System.out.println(className);
}
}
- 资源绑定器
ResourceBundle bundle = ResourceBundle.getBundle("fileTest");
String className=bundle.getString("className");
文件必须放在类路径下,扩展名必须是properties
super
与this的对比
this:
this能出现在实列方法中和构造方法中
this的语法是:this. this( )
this不能使用在静态方法中。
this()语句必须是构造器中第一个语句
public Date(){
this(1,2,3);
}
public Date(int i,int b,intc){
…
}
(this()实现代码复用)
super:
super能出现在实列方法中和构造方法中
super的语法是:super. super( )
super不能使用在静态方法中。
super( )语句必须是构造器中第一个语句,用以调用父类的构造方法。
当一个构造方法第一行:
即没有this()又没有super()的话,默认会有一个super();表示通过当前子类的构造方法调用父类的无参构造方法。所以必须保证父类的无参数构造方法是存在的。
this和super不能共存,都只能为构造器中第一个语句
用法
public Date1{
public Date1(int i,int b,intc){
…
}
}
public Date2 extends Data1{
public Date2( ){
super(1,2,3);
}
}
super什么时候能省略
Java中允许在子类中出现和父类一样的同名变量/同名属性
多数情况下都可以省略
子类中有和父类中同名属性,如果想在子类中访问父类的特征,super不能省略。
特点
super不是引用,也不保存内存地址,也不指向任何对象
super只是代表当前对象内部的那一块父类型特征
System.out.println(this);//可以执行
System.out.println(super);//不以执行
this
定义
一个对象一个this
this 是一个变量,是一个引用。this保存当前对象的内存地址,指向自身。所以,严格意义上来说,this代表的就是“当前对象”
this 存储在堆内存中的对象内部
只能使用在实列方法中
总结
可以通过当前构造方法调用本类中另一个构造方法
this();
this语句必须是构造器中第一个语句
//无参调用有参构造器
public Date(){
this(1,2,3);
}
public Date(int i,int b,intc){
…
}
使用
与C++类似
可以省略
static
规则
加static的变量叫做静态变量。静态变量在类加载时初始化,不需要new对象,静态变量的空间会自动开辟出来。静态变量储存在方法区。
使用
静态成员、静态方法都是与类相关的,访问时用“类名. "的方式访问。不需要new对象。
实列成员、方法需要new对象,才能访问。
实列的:一定需要“引用. ”来访问
静态的:使用“类名. "或者”引用. “都可以,建议使用”类名. “
属性什么时候使用static
每个对象都有各自的一份数据声明为实列变量;每个对象都一样声明为静态变量
方法什么时候使用static
此方法一般是描述了一个行为,如果说该行为必须由对象触发,那么该方法定义为实列方法。
当方法体中直接访问了实列变量,这个方法一定是实列方法。
空引用访问静态
c1=null;
Sustem.out.println(c1.name);
不会出现访问异常
因为静态变量不需要对象的存在
实际运行时还是:
System.out.println(student.name);
只有在”空引用“访问”实列相关的,都会出现空指针异常。
静态代码块
格式:
static { }
静态代码块在类加载时执行,并且在main方法之前执行
多个静态代码块一般是按照自上而下的循序执行
只执行一次
可用于写类的加载日志
实列语句块
InstanceCode
格式:
{
语句;
语句;
}
实列语句在类加载时并没有执行
只要是构造方法执行,必然在构造方法之前执行,自动执行实列语句块中的代码
面向对象
步骤
OOA:面向对象分析
QQD:面向对象设计
OOP:面向对象编程
特性
封装
继承
多态
get、set方法
开发规范
get方法要求:
public 返回值类型 get+属性名首字母大写(无参){ return xxxx; }
set方法要求:
public 返回值类型 set+属性名首字母大写(有一个参数){ xxxx= ; }
无论是否时用都需要定义,为以后封装提供访问方法
类
-
属性+方法
属性来源于:状态
方法来源于:动作 -
语法
创建对象的语法:
类名 对象名 = new 类名();
(new 出来的对象存储在堆中)s1叫做引用
使用:(与C++相似)
s1.属性 -
实列变量
实列变量实际上就是:对象级别的变量
不能通过类名来直接访问实列变量 -
构造方法
缺省构造器:系统自动提供的一个无参数的构造方法
语法:
[修饰符列表] 类名(形式参数列表){
}调用:
Student x=new Student ( );
通过new 来调用不需要指定返回类型
支持重载 -
类和类之间的关系
is a :
Cat is a Animal(猫是一种动物)
凡是能够满足is a的表示“继承关系”has a:
I has a Pen;
凡是能够满足has a关系的表示“关联关系”
关联关系通常以“属性”的形式存在like a:
Cooker like a FoodMenu(厨师像一个菜单一样)
凡是能够满足like a 关系的表示“实现关系”
实现关系通常是:类实现接口
封装
-
private
表示私有的,被修饰后,只能在本类中访问。
-
protected
-
public
继承
-
语法
关键字: extends
语法:
class 类名 extends 父类名 {
} -
作用
基本作用:子类继承父类,代码得到复用
主要作用:有了继承关系,才有了方法覆盖和多态机制
-
特性
父类 superclass
子类 subclassJava中只支持单继承,不支持多继承
Java中规定,子类继承父类,除构造放啊发不能继承之外,剩下都可以继承,但是私有的属性无法在族类中直接访问。
如果一个类没有继承任何一个类,则默认继承Object类
缺点:耦合度高
-
Object
-
toString
作用:把对象转换成字符串
Product pro = new Product();
String re = pro.toString();
System.out.println(re);结果:Product@135311
以下等同:
直接输出引用(会自动调用toString)
System.out.println(pro);可以通过覆盖来实现输出不同的东西
列如:Student st =new Student();
System.out.println(st);
输出学生的信息
-
方法覆盖和多态
-
方法覆盖
-
override
又会叫方法重写
(overload是方法重载)
1.定义区别:①重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数。
②覆盖(也叫重写)是指在派生类中重新对基类中的虚函数(注意是虚函数)重新实现。即函数名和参数都一样,只是函数的实现体不一样。
2.类的关系区别
覆盖是子类和父类之间的关系,是垂直关系;重载是同一个类中方法之间的关系,是水平关系。
3.产生方法区别
覆盖只能由一个方法或只能由一对方法产生关系;重载是多个方法之间的关系。 -
作用
与C++相似
-
要求
1、访问权限不能更低,但可以更高
权限高到低:
public > protected > private2、重写后的方法不能比之前的方法抛出更多的异常,但可以更少
-
注意
1、方法覆盖只针对方法,与属性无关
2、私有方法无法覆盖
3、构造方法不能被覆盖
4、方法覆盖只针对实列方法,静态方法覆盖没有意义
-
-
多态
Animal a2=new Cat();
a2.move ( );
Animal a3=new Bird();
a3.move ( );编译阶段:
对于编译器来说,编译器只知道a2的类型是是Animal,所以在编译阶段会去Animal.class字节码文件中找move( )方法,找到了,绑定上move( )
方法编译通过,静态绑定成功。(编译阶段属于静态绑定)运行阶段:
运行阶段的时候,实际上在堆内存中创建的java对象是Cat对象,所以move的时候,真正参与move的对象是一只猫,所以运行阶段会动态执行Cat对象的move( )方法。这个过程属于运行阶段的绑定。(运行阶段绑定属于动态绑定)多态表示多种形态:
编译的时候一种形态
运行的时候另一种形态-
基础语法
-
upcsting向上转型
子类----->父类(自动类型转换)
Animal a2=new Cat ( );
a2为父类型引用
new Cat ( );是一个子类型的对象父类型的引用允许指向子类型的对象
无论向上还是向下转型,两种类型之间必须有继承关系
-
downcasting向下转型
-
-
父类----->子类(强制类型转换,需要加强制转换符)
当需要访问子类对象中特有的方法。此时需要经行向下转型。
Animal a5=new Cat();
Cat x=(Cat)a5;
x.catMouse( );
但是
System.out.println();
Animal a6=new Bird();
Cat y=(Cat)a6;
y.catMouse();
会运行报错;
a6表面是Animal来信,运行时实际是Bird
所以编译器检测到a6是Animal类型引用,Animal和Cat之间存在继承关系,可以向下转型。
运行阶段,堆内存实际创建的对象是Bird对象,Bird与Cat对象没有继承关系,运行阶段出现异常。
避免出现此类异常
(a6 instanceof Cat表示a6是否为Cat类型是则返回true )
if ( a6 instanceof Cat ){
Cat y = ( Cat ) a6;
y.catMouse ( ) ;
}
- instanceof
(a6 instanceof Cat表示a6是否为Cat类型是则返回true )
if ( a6 instanceof Cat ){
Cat y = ( Cat ) a6;
y.catMouse ( ) ;
}
- 作用
提高扩展力,降低耦合度
方法
一般要求
方法定义在类体中
方法定义的先后顺序没有关系,
语法机制
【修饰符】 返回值类型 方法名(形式参数列表){ 方法体 }
调用
类名.方法名();
在调用本类的方法可以省略类名
方法名();
返回值
非void类型不能出现无返回值现象
public static int su(){
boolean flag = true;
if(flag){
return 1;
}
}
虽然flag为真,但是以上代码会编译出错,因为编译器不负责运行,只按规则。
参数传递
java中变量赋值原理:将变量中保存的值复制一份传递过去。
到“值”可能是:
一个数字
一个对象的地址
GC机制
主要针对堆内存
当对象没有任何引用指向该对象的时候,GC会考虑将该数据释放回收掉
XMind: ZEN - Trial Version