变量
-
变量名命名: 合法的标识符,默认规则首字母是小写后面的每个单词首字母大写。
-
变量的作用域:出了大括号,就没用了。
-
变量种类:成员变量 在整个类中有效 //实例变量 静态变量
局部变量:在方法内或方法内的某代码块(方法内部,“{”与“}”之间的代码)中声明的变量。在代码块声明的变量,只在当前代码块中有效;
数据类型
- 基本数据类型:
整数型:byte(一个字节)(-128~127 )
short(2),
int(4) , int a=4.5 输出a得到是4,int没有四舍五入
long(8);
浮点型:float(4)有效数字6~位 double(8)有效数字15位
布尔型: boolean(1)
字符型:char(2 ) 02^(15)-1(065535) 一个中文占两个字节,char可以存中文
java没有无符号数据类型
引用数据类型
- 引用数据类型:字符串String、java.math.BigDecimal类也是引用数据类型
java字符串由char值序列组成,char的数据类型是一个采用Unicode码点的代码单元。
byte和char的区别
char和byte这两种数据类型容易相互混淆,他们的区别主要如下:
1、数据类型不同
byte 是字节数据类型 ,是有符号型的,可以表示-128—127 的数;
char 是字符数据类型 ,是无符号型的,可以表示一个整数,不能表示负数。
用法举例:
char i = 85;//U对应ASCII是85
byte d3 = 127; // 如果是byte d3 = 128;会报错
byte d4 = -128; // 如果是byte d4 = -129;会报错
2、占用空间大小和大小范围不同
byte占1 个字节;大小范围为-128—127 ;
char占两个字节,大小范围0-65535
计算机存储数据用补码,正数的补码是本身,负数的补码是其绝对值的原码取反加一
逻辑运算符
与& 或| 非!
异或^ 短路与&& 短路或||
短路与和短路或和与还有或运算结果相同,但有时会发生短路现象
// int x = 10 ,y=8;
System.out.println(x<y & ++x<y); //false
System.out.println(x); //x=11
//int x = 10 ,y=8;
System.out.println(x<y && ++x<y); //false
System.out.println(x); //x=10 ++x<y被短路,没有运行。
//第一个表达式是false时,才会发生短路与,跳过第二个表达式
- ascll码 a->97 A->65 0->48
if的4种模板
//if(布尔表达式){
java语句
}
// if(){
Java语句
}else{
}
// if(){
java语句
}else if(){
Java语句
}else if() {
}
//if(){
java语句
}else if(){
Java语句
}else if(){
}else{
}
switch
//switch后面只能跟int和string类型,如果是char byte short
//会自动转换为int
switch语法结构: switch(int或string类型的变量){
case int或string类型的字面值:
java语句;
...
break;
case int或string类型的字面值:
java语句;
...
break;
case int或string类型的字面值:
java语句;
...
break;
default:
java语句;
}
//case 可以合并 int i = 10
switch( i){
case 1:case2: case3:
java语句; // 当i=1 2 3都执行当前语句。
}
while
while(布尔表达式){
java语句;
}
do{
java语句:
}while(布尔条件语句); //do while语句最后有分号
break;语句可以用于跳出循环 //一个break默认跳出当前的一个循环
//如果想要跳出指定的循环
for1:for(;;){
for2:for(;;){
java语句;
break for1 //跳出指定的外层循环。
}
}
*continue; //直接执行下一轮循环
用户键盘输入
public class KeyInputTest{
public static void main(String[] args){
java.util.Scanner s = new java.util.Scanner(System.in);//创建键盘扫描器对象 这里java.util.scanner是0包
int userInputContent = s.nextInt(); //用户输入并敲入回车后,输入信息赋值给userInputConetent
String str = s.next(); //接受文字,建立一个扫描器可以接受多个输入
System.out.println("您输入了:"+userInputContent);
}
}
函数
- java中方法(函数)的构建 //方法定义在类体当中,没有顺序区分,但不能在方法体中定义
- 方法定义的语法结构 修饰符列表 +返回值类型 + 方法名+(形参){
方法体;
}
访问权限
-
private: Java语言中对访问权限限制的最窄的修饰符,一般称之为“私有的”。被其修饰的属性以及方法只能被该类的对象 访问,其子类不能访问,更不能允许跨包访问。
-
default:即不加任何访问修饰符,通常称为“默认访问权限“或者“包访问权限”。该模式下,只允许在同一个包中进行访问。
-
protected: 介于public 和 private 之间的一种访问修饰符,一般称之为“保护访问权限”。被其修饰的属性 同包的类中创建的该类对象 和子类(不同包和同包)可以访问
-
public: Java语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不仅可以跨类访问,而且 允许跨包访问。
返回值
//返回值类型 所有数据类型都可以 如果不需要返回值,则用void 此时不能用return +值;但可以用renturn;结束方法
// 需要返回值时,用"renturn 值"
//当实参与形参不一致时,若实参是小类型,会自动转换,如果实参是大类型,需要强制转换才可以编译
//当方法有返回值时候,可以用变量接收返回值,但是变量类型和返回值要一致 如:int a = 类名.方法命(实参)
//方法代码片段存在哪里?方法执行的时候执行过程的内存在哪里分配?
方法代码片段属于.class文件的一部分,.class文件在加载时,将方法代码片段放到了方法区内存当中,
所以方法区内存最先有数据,存了方法代码片段。
//代码片段可以重复调用。每次调用该方法时,需要在栈内存中分配独立的空间(给方法运行的空间)
//调用方法时,在栈内存分配空间,此时发生入栈(压栈)动作,当方法执行结束,所分配内存释放,
发生出栈(弹栈)动作。
//局部变量在方法体中声明,运行时内存在栈中分配,方法结束后局部变量被释放
//方法调用的时候,参数传递时,传递的是变量保存的值,对形参进行操作不会改变实参变量保存的值
面向对象的三大特征: 封装、继承、多态
采用面向对象的方式开发一个软件,生命周期当中:
*面向对象的分析 OOA
*面向对象的设计 OOD
*面向对象的编程 OOP
- 类和对象的概念
什么是类?
*现实世界当中不存在,大脑虚拟抽象出来的结果
*类代表了一类事物
*在现实世界当中,对象a与对象b之间具有共同特征,进行抽象总结出一个模板,这个模板被称为类
什么是对象?
*对象是实际存在的个体。 对象又称为实例,对象到类的过程叫抽象化,类到对象的过程叫实例化
*重点,类描述的是对象的共同特征。
共同特征如:身高特征
这个身高特征访问时,必须先创建对象,通过对象去访问这个特征
因为这个特征具体到某个对象后,值不同。
一个类主要描述什么信息?
状态信息:名字、身高、性别、年龄
动作信息:吃,玩、学习
*类{
属性 //描述对象的状态信息
方法//描述对象的动作信息
}
类也属于引用数据类型
**对象的创建和使用
//首先创建一个学生类
public class Student{
//类体=属性加方法
//属性 储存数据采用变量的形式
//由于变量定义在类体中,方法体之外,这种变量称为成员变量
//成员变量没有赋值的话,系统会给默认值
//成员变量默认值 byte.short,int long 0
float double 0.0
boolean false
char \u0000
引用数据类型 null
//所有学生都有学号信息
//所以访问学号必须先创建对象,通过对象去访问学号信息
//学号信息不能直接通过类去访问,所以这种成员变量又叫 实例变量(对象变量)
//不创建对象,这个no变量的内存空间是不存在的,只有创建了对象,内存空间才会创建
int no;
int age; //年龄
String name; //姓名
}
///////////到此我们定义了一个名为Student的类,编译后生成.class文件,然后可以创建对象
public class OOTest01{
public static void main(String[] args){
//通过一个类可以实例化N个对象
//实例化对象的语法: new 类名();
// new运算符的作用时创建对象,在JVM堆内存当中开辟新的内存空间
//方法区内存:在类加载的时候,class字节码代码片段被加载到该内存当中
//栈内存(局部变量):方法代码片段执行的时候,会给该方法分配内存空间,在栈内存中压栈
//堆内存:new的对象在堆内存中存储
Student s = new Student(); //s是变量名 new Student()是学生对象,该行代码执行后
在堆内存创建一个叫s的Student对象
//s是局部变量,在栈内存储,它在栈内存中存了Student对象在堆内存中的地址,所以s被称为引用,java不能直接操作堆内存,
所以访问堆内存中的实例变量都要用引用 ,
***引用是指一个变量,该变量指向了一个对象,存放该对象的地址
访问实例变量的语法:
读取:引用.变量名
修改:引用.变量名=值;
//基本数据类型修改时,直接修改实例变量的值
//引用数据类型修改时,如String修改时,是先创建一个String对象,
存放修改的值,然后原引用指向该对象,存放String的地址
对象的封装性
封装步骤
1 所有属性私有化,使用private关键字进行修饰,修饰的所有数据只能在本类中访问,
而且在外部无法访问。(安全性) (私有化之后可以通过setter、getter和构造方法访问)
2 对外提供简单的操作入口,我们可以通过入口访问该属性(访问方式:读取get、修改set)
构造方法
- 构造方法又称为构造函数/构造器 //没有返回值
//因为返回值永远是对象的类型(引用数据类型),所以忽略
1、语法结构:
修饰符列表 + 构造方法名(形式参数列表){
构造方法体; //构造方法名与 对象所在的类名保持一致
}
*普通方法语法结构
修饰符列表 + 返回值类型 +方法名(形式参数列表){
方法体;
}
2、构造方法的作用:1通过构造方法的调用,可以创建对象
创建对象的同时,初始化实例变量的内存空间
构造器总是伴随着new运算符的执行被调用,所以不能对一个已 经存在的对象调用构造器来修改实例域
3调用 : 调用方法: new 构造方法名(实参)
-
当一个类中没有定义任何构造方法的话,系统默认给该类提供一个无参数的构造方法,这个构造方法叫做缺省构造器,缺省构造器会给该类的实例变量赋一个默认值(在该方法被调用时才赋值,类加载时不赋值)
-
无参构造器的作用:子类创建对象,使用自己的构造函数初始化时,会默认调用super(),但是如果父类此时没有该无参方法就会报错
-
当一个类有构造方法,系统不再默认为这个类提供无参数的构造方法,
建议开发中手动为当前类提供无参构造方法
构造方法支持方法重构(根据参数列表不同区分方法) -
构造方法无法被继承,因为子类的无参构造方法的第一条语句,默认包含了super(),会调用父类的无参构造方法,如果父类没有无参构造方法会报错。并且实例化子类的时候会调用父类的构造方法
-
对象和引用:
概念:对象:使用new运算符在堆内存中开辟的内存空间叫对象
引用:是一个保存对象地址的变量,可能时局部变量,也可能是成员变量
实例变量都要通过引用访问 -
关于java语言中this关键字:
- 1 this是一个引用,this是一个变量,保存了对象自身的内存地址,this储存在jvm的堆内存当中
- 2 java对象都有this
- 大部分this可以省略,用来区分局部变量的时候不能省略
- this的用法:1可以使用在实例方法当中,代表当前对象
2可以使用在构造方法当中,通过当前的构造方法调用其他 的构造方法;(语法:this(实参))
//且该种用法只能出现在构造方法的第一行
-
super(参数):通过子类中的构造方法调用父类中的某一个构造方法(应该为构造函数中的第一条语句,无论有没有参数),因此super()和this()在构造方法中不能共存。
-
super不是引用,里面存的不是内存地址,super代表的是当前子类型中的父类型特征,super可以用在成员方法和构造方法,不能用在`(静态方法中没有对象)
-
一个构造方法第一行,没有this 关键字,也没有super关键字,系统默认调用super()
类方法,静态变量,静态代码块
1、类方法是属于整个类,而不属于某个对象。
2、类方法只能访问类成员变量,不能访问实例变量,而实例方法可以访问类成员变量和实例变量。
3、类方法的调用可以通过类名.类方法和对象.类方法(引用.方法名,空引用也可以,,并不推荐),而实例方法只能通过对象.实例方法访问。
4、类方法只能访问类方法,而实例方法可以访问类方法和实例方法。
5、类方法不能被覆盖,实例方法可以被覆盖。
*带有static 的变量 叫静态变量
*静态变量储存在方法区当中,在类加载时初始化,不需要创建对象,因为它属于类,被该类的所有
对象共享。静态变量可以修改。可以通过类名.静态变量访问(所有对象的静态变量都一样),也可以通过对象.静态变量访问(不推荐)
*静态代码块: 语法 static{
java语句;
}
//静态代码块在类加载时候运行,且只运行一次,一般用于准备或者初始化
// 可用于类加载时,完成日志的记录
继承
-
Java的类默认继承object(一个库文件),一个子类只能继承一个父类,不支持多继承
-
继承语法
public class Cat(类名) extends Animal(类名) {
//猫继承了动物类
//猫是子类,继承了动物父类
}
------
public class Animal {
public void move(){
System.out.println("动物在移动");
}
}
------
public class Cat extends Animal {
public void move() {
System.out.println("猫在移动");//重写父类中继承过来的方法(不重写的话就继承父类原来的方法)
}
public void catchmouse(){
System.out.println("猫捉老鼠");
//不是从父类中继承的方法
//这个方法是子类对象特有的动作
}
}
//注意点:在方法覆写时必须考虑到权限问题,即:被子类覆写的方法不能拥有比父 类方法更加严格的访问权限。
public class Test(){
public static void main(String[] args) {
Animal a2 = new Cat();//这里是子类变父类,java允许父类型引用指向子类型对象
a2.move (); //该方法只能调用Animal中有的方法,而Cat的catchmouse方法不能调用,但是运行阶段中使用的是cat中的move,所以输出的是猫在移动。(堆内存中创建的是Cat对象)
//这种程序在编辑阶段和运行阶段的两种不同形态叫做多态机制
}
}
- 多态的语法机制
- 向上转型(upcasting) 子类型变为父类型(自动类型转换)
- 向下转型(downcasting) 父类型转换为子类型(需要强制类型转换符)
- 无论向上转型还是向下,都需要有继承关系
向下转型
需求:如果想要a2执行catchmouse方法,该怎么办
-
a2无法直接调用,animal对象中没有catchmouse
-
应该先将a2(Animal类型)转换为Cat类型
-
这是父类型转换为子类型,属于强制类型转换
-
当调用的方法是子类型特有的,父类型中不存在就需要用向下转型
-
代码如下 Cat c2 = (Cat)a2;
-
怎么避免转换异常?
使用instanceof运算符
语法 引用 instanceof 数据类型名
该运算式返回值只有true和false ,true是该引用指向该数据类型
false是该引用没有指向这个数据类型名
修改 : if(a3 instanceof Cat){
Cat c3 = (Cat) a3;//采用强制类型转换前建议使用instanceof
}
final关键字
关于final关键字的使用
-
public final class 类名{
} //final 修饰的类无法被继承
-
final修饰的方法无法被覆盖,父类中被final修饰的方法在子类不能重写//public final void 方法命{}
-
final修饰的变量赋值后,不可以重新赋值 //final int k=1;
-
实例变量如果要用final ,必须手动赋值,否则报错(实例变量有默认值,赋予默认值后,不能更改)
-
final 修饰的引用,指向某个对象后,不能更改,且被指向的对象无法被垃圾回收,除非main方法结束(地址不能更改),但是该引用指向的对象的实例变量可以更该
-
常量:当变量用final 和static修饰时称为常量,不可更改,且属于类,每个对象的该变量都一样
语法为:public static final 数据类型 变量名 = 值
常量的语法规范:常量名所有字母大写,单词之间用_分开。
抽象类
-
如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。它有以下特点:
1.抽象类可以有抽象方法,也可以没有。可以有普通方法,也可以没有。抽象类的子类可以实例化后调用抽象类中的方法
抽象方法的语法:在方法修饰符列表添加abstrac关键字,并且方法以;结束,而不是{}
//这也是抽象类public abstract class ClassName{ public void go(); }
2.抽象类不能实例化。但是可以有构造方法,给子类创建对象使用
ClassName cn=new ClassName();//错误写法,抽象类不能实例化。
3.普通类继承一个抽象类,必须实现其抽象方法(重写成非抽象方法)。可以不实现,这时普通类需要添加abstract变成一个抽象类。
( public) abstract Class 类名 extends ClassName{}
4.抽象类中可以有成员变量和成员方法。
public abstract class ClassName{ public int a; public void go(); public abstract void m1(); //抽象方法 }
5.一个普通类只能继承一种抽象类。
public class extends classname1,classname2//错误写法6.一个抽象类遵循一个接口,可以不实现其抽象方法。抽象类不一定有抽象方法,但是抽象方法必须存在抽象类当中
public abstract class ClassName implements interface {}
// 这就是一个抽象类 abstract class Animal { String name; int age; // 动物会叫 public abstract void cry(); // 不确定动物怎么叫的。定义成抽象方法,来解决父类方法的不确定性。抽象方法在父类中不能实现,所以没有函数体。但在后续在继承时,要具体实现此方法。 }
接口
- 接口也是一种引用类型,可以等同于类(特殊的抽象类,特殊在接口时是完全抽象的,没有构造方法且和抽象类一样无法被实例化)一般来说抽象类的功能比接口多,因为抽象类可以有方法,但是抽象类不可以多继承, 接口可以
- 接口语法 修饰符 + interface +接口名{}
- 接口中只能出现常量和抽象方法 ,接口中常量的 public static final int a = 100 前面的public static final 可以省去 抽象方法的 public abstract 也可以省去(public abstract void m2(); -> void m2();)
- 接口和接口之间可以多继承
- 一个类可以实现(继承)多个接口,接口可以多继承
- 一个非抽象的类实现接口,需要将接口中所有的方法实现(重写),抽象类可以不实现
object超类
1、关于Object中的toString方法
public String toString(){
return getClass().getName() + “@”+Integer.toHexString(whis.hashcode());
}
作用:返回java对象的字符串表形式,返回格式位类名+@+java对象的内存地址经过哈希算法得出的int类型值再转换为十六进制
一般由于toString方法实现的结果不满意而重写
-
2 Object中的equals方法
-
public boolean equals(Objet obj){
return(this == obj);
//==两边如果是引用,则比较内存地址,相同返回true,不同返回false
}
-
equals 比较的是内存地址,现实中一般比较内容,所以需要重写
class Person {
int id;
String name;
public Person(int id ,String name){
this.id =id;
this.name =name;
}
public boolean equals(Object obj){
if (this==obj) return true; //如果内存地址一致,一定是true
//重写equals方法,当内容一致时返回true
if(obj instanceof Person){ //因为Object中没有id属性,需要强制转化成Person类
Person s =(Person) obj;
if(this.id ==s.id&&s.name.equals(name)) //s.name 是字符串,这里比较内容
return true
}
return false;
}
}
---------------------------------
关于java中如何比较两个字符串是否相等,不能使用==比较 ,因为String变量是引用数据类型,双等号比较的是地址
String 类中重写了equals方法 ,比较的是内容
因此 String s1 = new String("abc");
String s2 = new String ("abc");
s1.equals(s2); // ture
- 3 finalize方法:每个对象都有,当对象没有引用指向该对象时,变成垃圾,等待垃圾回收器(GC)调用
finalize回收,程序员不能自己调用finalize,只能建议系统启动垃圾回收器
finalize 在Object 中的方法
protected void finalize() throws Throwable{}
重写finalize方法 还可以在对象被回收前执行希望的操作,如输出对象被回收的消息,或者”挽救“对象
public class Test{
public static void main (String[] args){
Person p = new Person();
p =null;
System.gc(); //这里是建议系统启动回收垃圾回收器
}
}
class Person{
//重写回收方法;
protected void finalize() throws Throwable{
System.out.println("this+
。”被回收“"); // Person@ead456被回收
//挽救该对象
}; //注意这里有分号
}
4、hashcode 方法返回对象的哈希码值(用十六进制表示)
哈希码值 :对象的内存地址通过哈希算法得出的int类型的数值
5、Object中 的clone()方法 : 创建并返回对象的一个副本,对副本进行操作不会改变原来对象的数据
内部类
静态内部类
//静态内部类
//作用:可以访问外部类中私有的变量数据,(内部类在外部类中)(只能访问外部类的静态数据,无法直接访问实例数据)
//静态内部类等同于静态变量
public class OuterClass{
//静态变量
private static String s1 = "a"
//成员变量
private String s2 ="b";
//静态方法
public static void m1(){
sys "m1方法执行"
}
//成员方法(实例方法)
public void m2(){
sys "m2方法执行"
}
//静态内部类,可以用控制修饰符修饰
static class InterClass{
//定义静态方法
public static void m3(){
System.out.println(s1); //输出a
System.out.println(s2); //编译错误 静态类无法访问成员变量
m1(); //调用m1方法 输出m1方法执行
m2(); // 编译错误,无法调用成员方法
}
//定义成员方法
public void m4(){
sys "m4方法执行"
}
}
//入口
public static void main(String[] args){
OuterClass.InterClass.m3(); //执行m3静态方法
InterClass ic = new OuterClass.InterClass();
ic.m4(); // 执行m4成员方法
}
}
成员内部类
//成员内部类,也可以等同看作成员变量
//成员内部类无法声明静态方法,和静态变量
//作用:可以访问外部类中所有的变量数据,
public class OuterClass{
//静态变量
private static String s2 = "a"
//成员变量
private String s1 ="b";
//静态方法
public static void m1(){
sys "m1方法执行"
}
//成员方法(实例方法)
public void m2(){
sys "m2方法执行"
}
//成员内部类,可以用控制权限修饰符修饰
class InterClass{
//定义成员方法
public void m4(){
sys "m4方法执行"
}
}
//入口
publib static void main(String args){
OuterClass oc = new OutClass(); //创建外部类的对象
InterClass inter= oc.new InterClass(); // 创建内部类对象
Inter.m4 // 调用m4
}
局部内部类
//局部内部类等同于局部变量
public class OuterClass{
public void m1() {
int i = 1; //局部变量
final int j = 1; //局部变量
//局部内部类, 无法加控制权限修饰符(局部变量只在当前方法体中有效)
//局部内部类中不能有静态声明(局部内部类只在当前方法体有效,不能声明静态变量和静态方法)
class InnerClass {
public void m2() {
// System.out.println(i); //编译错误,局部内部类访问局部变量时,局部变量要使用final修饰
//在java8中,可以不使用final,如果局部变量被内部类 // 访问,那么该局部变量相当于自动使用了final修饰。
System.out.println(j);//编译通过,
}
}
InnerClass inner = new InnerClass(); //由于局部内部类只在m1内有效,无法在其他地方创建
//只能在m1中创建对象,调用m1方法时可以调用m2
inner.m2(); //调用m2
}
public static void main(String[] args){
//如何调用m2?
OuterClass oc = new OuterClass();
oc.m1();
}
}
匿名内部类
public class Test{
// 静态方法
public static void t(CustomerService cs){
cs.logout();`
}
//入口
public static void main(String[] args){
//调用t方法
t(new CustormerServiceImpl());
}
}
//接口
interface CustomerService{
//退出系统
void logout();
}
//编写一个类继承CustornerService接口
class CustormerServiceImpl implements CustormerService{
public void logout{
sys "退出"
}
}
------------------------------------------------------
//如果不想编写一个类继承接口,可以使用匿名内部类的方法
//优点可以少编写一个类,确定匿名内部类无法重新使用
public class Test{
// 静态方法
public static void t(CustomerService cs){
cs.logout();
}
//入口
public static void main(String[] args){
//调用t方法
//匿名内部类,该类没有名字,重写了接口的方法
t(new CustomerService(){
public void logout(){
System.out.println("tuichu");
}
});
}
}
//接口
interface CustomerService{
//退出系统
void logout();
}
异常
-
java中用类的模式模拟异常,一个类代表一类异常, 异常类创建对象表示具体的异常事件
NullpointerException e = 0x1234 该地址指向一个对象,该对象表示异常事件
异常可以自定义处理方式
-
异常的层次结构 throwable (extends Object) 所有的异常都是可抛出的
该图都有继承关系
Error继承throwable 不可处理 出现该类异常,一定会退出jvm
Exception 可处理的,如果没有处理会退出jvm
runtimeexception extend exception 运行时异常
checked Exception 编译时异常:必须在编写程序阶段进行处理,
处理方式有两种:捉和抛出异常声明。捕捉:try catch
声明抛出:在方法声明的位置上使用throws关键字抛出异常
public static void main(String[] args) {
FileInputStream fls = new FileInputStream("c:/user/xxx");
//如果文件路径出错,会出现编译时错误
}
解决方法1 用throws关键字向上抛出异常
public static void main(String[] args) throws Exception {
FileInputStream fls = new FileInputStream("c:/user/xxx");
//Exception 是 FileInputStream的父类
//两者不是直接继承,但子类的子类依然是也是父类的子类
//Exception的子类是IoException类 IOException类是 FileInputStream的父类
}
try catch
/*语法:try{
可能出现异常的代码;
}catch(异常类型 变量){
处理异常的代码;
}catch(异常类型2 变量){
处理异常的代码;
}
* 捕获类型要从小到大捕捉,先捕捉FileNotfound再捕捉IOException (IO类包括了FileNotFound)
*最多执行一个catch语句 执行后try catch语句结束
*/
//以下程序成功捕捉异常,编译通过
public class ExceptionTest{
public static void main(String[] args){
try{
FileInputStream fls = new FileInputStream("C:/ddd")
}catch(FileNotFoundException e){
sys“没有找到指定文件”
}
}
}
//以下程序编译不通过,因为还有其他IOException类型的异常没有处理
//需要catch IOException
public class ExceptionTest{
public static void main(String[] args){
try{
FileInputStream fls = new FileInputStream("C:/ddd")
fls.read();
}catch(FileNotFoundException e){
sys“没有找到指定文件”
}
}
}
//修改
public class ExceptionTest{
public static void main(String[] args){
try{
FileInputStream fls = new FileInputStream("C:/ddd")
fls.read();
}catch(FileNotFoundException e){
sys“没有找到指定文件”;
System.out,println(e); //一般打印引用会调用引用的Tostring()
//这里的e是一个引用,指向jvm中创建的一个异常类型对象
//一般toString方法默认输出引用存储的内存地址,但这里是输出出现的异常情况
// 说明FileNotFound将object中的toString方法被重写了
}catch(IOException e){
sys"其他IO类异常 "
}
}
}
finally语句
// finally 语句可以直接和try语句联用。
//try... catch... finally...也可以
//finally 语句中的代码块是一定会被执行的
import
public class Test throws Exception{
public static void main(String[] args){
/*try{
Sys "abc";
return;
}finally{
sys "ddd"; // 输出 abc ddd 虽然try中有return语句
finall中的语句还是执行
}*/
/*try{
FileInputStream fls = new FileInputStream("c:/ddd")
Sys "abc"; //由于上行代码出现异常,本行语句不执行
}
}finally{
sys "ddd"; //本行语句依然执行
*/
/*try{
System.exit(0);//退出jvm
}finally{
sys "ddd"; //当try语句退出jvm,本行语句不执行
} */
}
}
自定义异常
//自定义无效名字异常类
public class IllegalNameException extends Exception{
//定义异常一般提供两个构造方法
public IllegalNameException(){}
public IllegalNameException(String msg){
super(msg);
}
}
————————————
//顾客相关业务
public class CustomerService{
public void register(String name) throws IllegalNameException{
if (name.length()<6){
//用户名长度小于6 异常
//创建异常对象
IllegalNameException e = new IllegalNameException("用户名长度不能小于6!");
//手动抛出异常
throw e; //因为是手动抛出,所以这里不用trycatch,
//上面两句可以写为 throw new IllegalException("用户名长度不能小于6!");
//上面两行代码可以简写为 throws new IllegalNameException("用户名长度不能小于6!");
//关于异常的处理,这里时编译时异常,需要处理,否则jvm会退出,如果要在本类处理异常,用
//trycatch,如果想将异常信息传给其他类或者在其他地方处理, 用throws
}
//如果代码可可以执行到这,说明用户名合法
System.out.println("注册成功!");
}
}
____________
//模拟注册
public class Test{
public static void main{String[] args}{
//假如用户提供的用户名如下
String username = "jack";
//注册
CustomerService cs1 = new CustomerService();
try{
cs1.register(username); //可能出现异常的代码
}catch(IllegalNameException e){
System.out.println(e.getMessage());
}
}
}
数组
-
数组: 数组是一种引用类型,一种简单的线性数据结构,用于存储其他元素,数组储存在堆中
-
数组初始化后,长度是固定的。不可改变
-
数组将首元素的内存地址作为数组的内存地址,是首元素的地址,而不是首元素保存的值(地址)
静态初始化语法: int[] a = {0,1,2};
动态初始化语法: int[] a = new int[4]; //声明一个可以存储4个int类型的数组
静态初始化和动态初始化存储方式一样,如果事先知道数组存储什么数据,可以用静态初始化,不知道用动态初始化。
数组拷贝函数
/*
关于数组的拷贝
*/
public class Test{
public static void main(String[] args){
//语法
//System.arraycopy(源数组,源数组的开始复制下标,目标数组,目标数组的开始粘贴下标,拷贝的长度)
int[] src = {2,3,4,5,6,7};
int[] dest = {10,55,78,94,97,98,10};
//把src中456 拷贝到dest中,从94开始
System.arraycopy(src,2,dest,3,3);
}
}
常用类的使用
//String 类
//String 类是不可变类,不能被继承
/*例如 字符串"abc"一旦创建,不可以改为"ab",这里的不可改变是字符串abc的值不可改变(String 的底层数据结构是用final修饰的char数组),我们当然可以修改字符串引用指向的内存来修改 例如 String s = "abc"; s="ab";这里只是让s重新指向ab
*/
//为了提高字符串访问效率,java在程序中使用了“缓存”技术。所以在java中所有用"双引号"括起来的字符串
都会在字符串常量池中创建一份,字符串常量池在方法区中被存储,创建字符串时,会现在常量池寻找是否存该
//字符串,没有就创建,有就直接使用
String s1 = "abc"; //这种只会在方法区的常量池创建
String s2 = new String("ab"); //这种创建方式不仅在常量池创建,也会在堆区中创建,相当于创建了两个 //对象,但s2引用指向堆区的对象
//面试题
public class Test{
public static void main(String[] args){
//判断以下语句创建了几个对象? 3个
//堆中两个
//方法区字符常量池中一个
String s1 = new String("ab");
String s2 = new String("ab");
}
}
------------------------------------------
关于字符串常用的构造方法
//1 String s1 ="abc";
//2 String s2 = new String("abc");
//3 String(byte[] ascii) // 将传入的字符转换成ascll码
byte[] bytes = {97,98,99,100};
String s3 = new String(bytes);
System.out.println(s3);// abcd
//String 已经重写了Object中的toString方法
//4 String s4 = new String(bytes,0,2); //从下标0开始转换,转换2个
System.out.println(s4);// ab
//5 char[] c1={'我','是','中','国','人'};
String s1 = new String(c1);
System.out.println(s1); //我是中国人
//6 char[] c1={'我','是','中','国','人'};
String s1 = new String(c1,2,2);
System.out.println(s1); //中国
----------------------------------------------------
String中常用的方法
1 // charAt(int index) 返回指定索引处的char值
char c1 = "abcded".charAt(2);
System.out.println(c1); // c
2 // (boolean)返回值
// endsWith(String suffix) 测试次字符串是否以指定后缀结束
System.out.println("helloworld".endsWith("java")); //true
3//检测字符串s和字符串t是否相等 s.equals(t) // 如果相等返回true 不等返回false s和t可以是字符 串引用,也可以是字符串 如“hello”.equals(t)
4(boolean) equalsIgnoreCase(String anotherString) 比较两个字符串是否相等,忽略大小写
System.out.println("HelloWorld".equalsIgnoreCase(helloworld)); // true
5 (byte[]) getBytes() 将字符串转换为byte数组 (ascll)
byte[] bytes = "abc".getBytes();
for(int i = 0;i<bytes.length;i++){
System.out.println(bytes[i]);
} //97
//98
//99
6 (int) indexOf(String str) 返回指定字符串在此字符串中第一次出现的索引
System.out.println("helloworld".indexOf("el")); // 1
7 (int) indexOf(String str,int formIndex) 返回指定字符串在此字符串中第一次出现的索引,从指定的
索引开始检索
8(int) lastIndexOf(String str) 返回指定字符串在此字符串中最后一次出现的索引
9 (int) length(); 数组是length属性 ,字符串是length方法
10 (String) replaceAll(String regex,String replacement)
// 使用给定的replacement替换此字符串所有匹配给定的正则表达式的子字符串
System.out.println("helloworld hellojack".replaceAll("hello","hi"))
//hiworld hijack
//这个语句执行后创建了4个字符串对象
11 (String[]) split(String regex) 根据给定正则表达式的匹配拆分此字符串 返回值是字符串数组
String myTime = "2019,10,09";
String[] ymd = myTime.split(",");
for(int i=0;+i<ymd.length;i++){
System.out.println(ymd[i]);
} //2019
10
09
//String[] args 在接收参数时,就是以空格为界限分割我们输入的字符串 ,存入args数组中
12 (boolean) startWith(String prefix) 测试此字符串是否以指定的前缀开始
13 String subString(int beginIndex) 返回一个此字符串的子字符串,从给定的索引开始截取
14 String subString(int beginIndex,int endIndex) 后面的参数不包含
15 (char[]) toCharArray() 将此字符串转换为一个新字符数组
char[] c1 = "helloworld".toCharArray;
16 (String) toUpperCase() //将此字符串转换为大写
17(String) toLowerCase() //将此字符串转换为小写
18(String) trlm(); //去除字符串前后全部的空格
System.out.println(" dasd da ".trlm);//dasd da
19(static String) valueOf(Object obj); 返回Object参数的字符串表现形式 //带有static的方法调用时用类名.
Object o =new Object();
System.out.println(o); //不报异常
System.out.println(String.valueOf(o));//不报异常
System.out.println(o.toString());// 空指针异常
* 正则表达式
是一种字符模型,专门做字符串格式匹配的
是一门独立的学科
例如 "a(2)$" 表示两个a字符 等同于"aa"
\d 数字
\D 非数字
\w 英文字母
\W 非英文字母
String类中有一个方法
(boolean) matches(String regex) 告知此字符串是否匹配给定的正则表达式regex
--------------------------------------------------------
StringBuffer 和 StringBuilder
java.lang.StringBuffer
java.lang.StringBuilder
1 StringBuffer 和 StringBuilder是一个字符串缓冲区
2 工作原理 预先在内存中申请一块空间,以容纳字符序列,存储在字符串常量中,如果预留的空间不够
还可以自动扩容(数组的扩容方式是把原数组拷贝到一个更大容量的新数组)
3 StringBuffer 和 StringBuilder与String的区别
String是不可变的字符序列(final char),存储在字符串常量池中
StringBuffer底层是一个char数组,但是该数组是可变的,且可以自动扩容
StringBuffer 默认容量是16个char类型元素,也可以自定义容量
4 StringBuffer是线程安全的
StringBuilder是非线程安全的,在多线程情况下使用可能会出现异常
// 创建字符串缓冲区对象
StringBuffer sb = new StringBuffer(100);
// 可以向StringBuffer对象追加字符串
//推荐使用StringBuffer拼接字符,这样不会产生大量字符串,占用内存
String[] ins = {"体育","music","dance"};
for(int i=0;i<ins.length;i++){
if(i==ins.length-1){
sb.append(ins[i]);// 将char参数的字符追加到StringBuffer
}else {
sb.append(ins[i]);
sb.append(",");
}
}
-------------------------------------------------------------
8种基本数据类对应的包装类
基本数据类型 包装数据类型
byte java.lang.Byte
short java.lang.Short
int java.lang.Integer
long java.lang.Long
float java.lang.Float
double java.lang.Double
boolean java.lang.Boolean
char java.lang.Character
作用,当方法需要引用参数时(byte int short等无法满足要求,需要使用包装类)
//需要希望m1方法可以接受任何一种数据类型
//可以先把byte包装成java.lang.Byte,在传进m1
public static void m1(Object o){
System.out.println(o);
}
//b是基本数据类型
byte b = 10;
//b1是引用
Byte b1= new Byte();
m1(b1); //10 Byte将Object的toString方法重写了
//Integer类详解(其他包装类与其类似)
public class Test{
public static void main(String[] args){
1 // 获取int类型的最大最小值
System.out.println("int的最少值:"+Integer.MIN_VALUE);
System.out.println("int的最大值:"+Integer.MAX_VALUE);
2 构造方法 //创建Integer对象
//Integer(int value) 构造一个Integer对象,它表示指定的int值
//Integer(String s) 构造一个Integer对象,它表示String参数所指的int值
Integer i1 = new Integer(10); //装箱
INteger i2 = new Integer("10");
3//Integer常用方法
1//基本数据类型-> 引用类型
Integer i1 = new Integer(10); //装箱
2//int intValue(); 以int类型返回该Integer对象的值
//引用类型转换为基本类型
int i2 = i1.intValue(); //拆箱
3//(static int) int parseInt(String s);
//String-> int //该字符串必须是数字字符串
int age = Integer.parseInt("25");
System.out.println(age+1); // 26
4//(static double) parseDouble(String s);
// String -> double
5 //(static String) toBinaryString(int i)
// 将int类型的值转换为二进制,再以字符串形式返回
6(static Integer) valueOf(String s)
//把String转换为Integer
7(static Integer) valueOf(int i)
//把int转换为Integer
}
}
//byte[]转成String
new String (byte[])
byte 转成string
byte b = 'a';
string str = b+"";
*//重点 3种类型的互换
//1 int-> Integer
Integer i1 = Integer.valueOf(10);
//2 Integer->int
int i2 = i1.intValue();
//3 String->Integer
Integer i3 = Integer.valueOf("10");
//4 Integer->String
String s1 = i3.toString();
//5 String->int
int i4 = Integer.parseInt("123");
// int->String
String s2 = 10+"";
-------------------------------------------------------
jdk1.5后可以自动装箱和自动拆箱
// jdk5.0之前
// Integer i1 = new Integer(10);
// int i2 = i1.IntValue();
//jdk1.5之后 , 相当于自动类型转换
Integer i3 = 10; //自动装箱
int i4 =i3 // 自动拆箱
面试题// 再-128到127之间,java引入了一个整型常量池,和字符串常量池一样,指向常量池中同一个数据的引用,保存的地址是一样的
Integer i5 = 127; //不会在堆中创建对象,在常量池中直接调用数据
Integer i6 = 127;
System.out.println(i5==i6); //true
Integer i7 = 128; // 创建对象
Integer i8 = 128; //创建对象
System.out.println(i7==i8); //false (toString方法中,两个引用指向不一致)
//日期类
java.lang.System类下有一个 (static long) currentTimeMillin()方法 返回以毫秒为单位的当前时间
获取自1970年1月1日0时0分0秒到当前时间的毫秒数
java.util.Date 构造方法 Date()
Date time = new Date();
System.out.println(time); // Mon Oct 14 17:54:59 CST 2019
//结果不满意 ,再次重写日期格式
/*
Y 年
M 月
d 日
H 消失
m 分
s 秒
S 毫秒
E 星期
这格式是在text定义的
*/
import java.util.Date;
import java.text.SimpleDateFormat
Date nowTime = new DAte();
//java.text.SimpleDateFormat
创建日期格式化元对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年M月dd日 HH:mm:ss:SSS E");
//开始格式化
String strTime = sdf.format(nowTime);
System.out.println(strTime);//2019年10月14日 20:01:44:562 星期一
--------------------------------------------------------
获取指定的时间
import java.util.Date;
import java.text.SimpleDateFormat;
String strtime = "2019年10月14日 20:14:51 000";
//将String日期转换为日期类型Date
//1创建日期格式化对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年M月dd日 HH:mm:ss:SSS E");
//将字符串转换成日期类型
Date t = sdf.parse(strTime);
System.out.println(t);
______________________________________________________
//Calendar日历类
import java.util.Calendar;
public class Test{
public static void main(String[] args){
//获取系统当前日历
Calendar c = Calendar.getInstance();
//查看当前的日历是星期几
Calerdar i = c.get(Calendar.DAY_OF_WEEK);
sys(i) // 3
//获取2008 02 14 是星期几
String strTime ="2008,02,14";
//将字符类型转换为日期类型
Date d = new simpleDataFormat("yyyy,MM,dd").parse(strTime);
//设置日历
c.setTime(d);
//输出星期几
System.out.println(c.get(Calendar.DAY_OF_WEEK));//访问常量前面要带类名
}
}
--------------------------------------------------------------------
//数字类
数字格式化类
java.text.DecimalFormat
数字格式/* # 任意数字
, 千分位
. 小数点
0 不够补0
*/
//例子
//创建数字格式化对象
DecimalFormat df = new DecimalFormat("###,###");
/.开始格式化
System.out.println(df.format(1234567)); // 1,234,567
//保留两位小数
DecimalFormat df1 = new DecimalFormat("###,###.##");
------------------------------
java.math.BigDecimal
//用于财务领域的高精度数据类
---------------------------
随机数
//Random()
(int) nextInt(int n) 返回一个从0(包括)到n(不包括)的伪随机数
(伪随机数是用确定性的算法计算出来自[0,1]均匀分布的随机数序列)
//创建一个随机数生成器
Random r = new Random();
//生成int类型的随机数
int i= r.nextInt(10); //从0到9的随机int类型数据
-----------------------------------------------------------
枚举类型
//可以用于多种情况的判断
enum Result{
//成功和失败
//规范要求: 大写 有限个
SUCCESS,FALL
}
public class Test{
public static void main(String[] args){
int a= 10;
int b= 0;
//用Result类型来接受枚举类型返回值
Result retValue = divide(a,b);
if(retValue == Result.SUCCESS){
Sys("成功");
}else if (retValue== Result.FALL){
sys("失败");
}
}
public static Result divide(int a ,intb){//返回值是Result
try{
int c=a/b;
return Result.SUCCESS;
}catch(Exception e ){
return Result.FALL;
}
}
}
集合
java集合中有三种重要的类型
// List(接口): 有序集合,可以批放重复的数据(有序指存进去和取出的顺序一样)
// Set (接口) 无序集合,不允许放重复的数据
// Map 无序集合,集合中包含一个键对象和一个值对象,键对象不可以重复,值对象可以3
//集合只能存储引用
//List和Set属于Collection接口,一个一个的引用存取
//Map是一对一对引用储存
collection接口
boolean add(E e)
确保此集合包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c)
将指定集合中的所有元素添加到此集合(可选操作)。
void clear()
从此集合中删除所有元素(可选操作)。
boolean contains(Object o)
如果此集合包含指定的元素,则返回 true 。
boolean containsAll(Collection<?> c)
如果此集合包含指定 集合中的所有元素,则返回true。
boolean isEmpty()
如果此集合不包含元素,则返回 true 。
Iterator<E> iterator()
返回此集合中的元素的迭代器。
boolean remove(Object o)
从该集合中删除指定元素的单个实例(如果存在)(可选操作)。
boolean removeAll(Collection<?> c)
删除指定集合中包含的所有此集合的元素(可选操作)。
default boolean removeIf(Predicate<? super E> filter)
删除满足给定谓词的此集合的所有元素。
boolean retainAll(Collection<?> c) 取交集 比如 lista.retainAll(listb) 返回lista与listb的交集,返回给lista
仅保留此集合中包含在指定集合中的元素(可选操作)。
int size()
返回此集合中的元素个数。
Object[] toArray()
返回一个包含此集合中所有元素的数组。
<T> T[] toArray(T[] a)
返回包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。
import java.util.*;
public class Test{
public static void main(String[] args){
//创建集合
Collection c = new ArrayList();//多态Collection是接口,ArrayList是其中一个实现类
//添加元素
c.add(1); //自动装箱,1转换为Integer类型
c.add(new Object());
//将集合转换为Object类的数组
Object[] objs = c.toArray();
//使用迭代器遍历c集合
//1获取迭代器对象
Iterator it = c.iterator(); it是引用,指向了堆中一个迭代器对象
//开始迭代(while循环)
//ArrayList存进去的顺序和取出一致
while(it.hasNext()){ //判断是否有更多的元素
Object element = it.next(); //将迭代器向下移动一位,并取出元素
System.out.println(element);
}
//for循环迭代
for(Iterator it =c.iterator();it.hasNext;){
Object element = it.next();
System.out.println(element);
}
}
}
list集合
//List集合
//有序
//可重复
//创建List集合
List l = new ArrayList();
//ArrayList默认初始化容量是10,扩大之后的容量是扩大前的1.5倍
//Vector 初始化容量是10 ,扩大倍数是2倍 线程安全
//可以事先定义容量,减少扩大容量次数,可以节省内存
get(int i ) 返回下标为i的元素
---------------------------------
//Set接口
HashSet底层实际上是HashMap(key通过对象获取)
HasdMap底层是Entry[]数组(哈希表) Entry是单向链表
//哈希表是一个存储单向链表的数组
//哈希表中,同一个单链表中的元素,key一定不同,hash值相同,hash值是通过key通过hash算法得出的
key调用hashcode(),再通过hash function转换成
//添加元素时,当通过key得出hash值后,要查看该hash值是否有对应的单链表,没有就创建,如果有,要使用equals比较该元素,与该链表其他元素的key值是否相同,不同就加入链表中,相同则放弃添加该元素
//HashSet和HashMap初始化容量是16,默认加载因子是0.75,(当容量到达75%时开始扩容)
//不可重复性
Set s = new HashSet();
s.add(1);
s.add(1); //集合元素不可重复,但编译可以通过
//遍历
Iterator it = new iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//关于Set集合中hashCode和equals方法
import java.util.*
public class SetTest{
public static void main(String[] args){
//创建集合
Set s = new HashSet();
Employee e1 = new Employee("1000","jack");
Employee e2 = new Employee("1000","jack");//由于equals和hashcode没有重写,导致集合在业务逻辑上出现了可重复的情况,e1和e2数据相同,内存地址不同,得出的hash值也不同
Employee e3 = new Employee("1001","com");
s.add(e1);
s.add(e2);
s.add(e3);
//查看元素个数
System.out.println(s.size());
}
}
//员工类:需求员工编号是1000-9999
public class Employee{
String no;
String name;
Employee(String no,String name){
this.no = no;
this.name = name;
}
//重写equals和hashCode()
public boolean equals(Object o){
if (this == o){
return true;
}
if(o instanceof Employee){
Employee e = (Employee)o;
if(e.no==this.no&&e.name&&this.name){
return true;
}
return false;
}
}
public int hashCode(String no){
//以员工编号返回hash值
return no.hashCode();
}
}
HashMap
遍历map的方式
Person p1=new Person( 1,"what");
Person p2=new Person( 1,"张明");
Map<Integer,Person> map=new HashMap<Integer, Person>();
map.put( 1, p1);
map.put( 2, p2);
//遍历map集合
for(Map.Entry<Integer, Person> a:map.entrySet()){
System.out.println("键是"+a.getKey());
System.out.println("值是"+a.getValue());
}
方法二
Map map = newHashMap();
map.put( 1, "Jacky");
map.put( 2, "Lucy");
map.put( 3, "Tom");
Iterator it = map.entrySet().iterator();
while(it.hasNext()){
Map.Entry entry = (Map.Entry) it.next();
System.out.println(entry.getKey() + " : " + entry.getValue());
}
方法三
Iterator it=map.keySet().iterator();
while(it.hasNext()){
//取出key
String key=it.next().toString();
System.out.println(key);
//通过key拿到value
String str1=(String) map.get(key);
System.out.println(str1);
}
//关于Map集合中的常用方法
void clear()
从该地图中删除所有的映射(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射,则返回 true 。
boolean containsValue(Object value)
如果此地图将一个或多个键映射到指定的值,则返回 true 。
Set<Map.Entry<K,V>> entrySet() //返回key和value的关系(key=value)
返回此地图中包含的映射的Set视图。
boolean equals(Object o)
将指定的对象与此映射进行比较以获得相等性。
default void forEach(BiConsumer<? super K,? super V> action)
对此映射中的每个条目执行给定的操作,直到所有条目都被处理或操作引发异常。
V get(Object key) //通过key获取value
返回到指定键所映射的值,或 null如果此映射包含该键的映射。
default V getOrDefault(Object key, V defaultValue)
返回到指定键所映射的值,或 defaultValue如果此映射包含该键的映射。
int hashCode()
返回此地图的哈希码值。
boolean isEmpty()
如果此地图不包含键值映射,则返回 true 。
Set<K> keySet() //获取Map中所有的key
返回此地图中包含的键的Set视图。
default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
如果指定的键尚未与值相关联或与null相关联,则将其与给定的非空值相关联。
V put(K key, V value) //添加键值对
将指定的值与该映射中的指定键相关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
将指定地图的所有映射复制到此映射(可选操作)。
default V putIfAbsent(K key, V value)
如果指定的键尚未与某个值相关联(或映射到 null )将其与给定值相关联并返回 null ,否则返回当前值。
V remove(Object key)
如果存在(从可选的操作),从该地图中删除一个键的映射。
default boolean remove(Object key, Object value)
仅当指定的密钥当前映射到指定的值时删除该条目。
default V replace(K key, V value)
只有当目标映射到某个值时,才能替换指定键的条目。
default boolean replace(K key, V oldValue, V newValue)
仅当当前映射到指定的值时,才能替换指定键的条目。
int size() //获取键值对个数
返回此地图中键值映射的数量。
Collection<V> values()
获取所有value
//存储在Map集合key部分的元素需要重写hashCode和equals方法
//例子
import java.util.*;
public class Test{
public static void main(String[] args){
//创建Map集合
Map persons = new HashMap();
//HashMap的默认初始化容量是16;默认加载因子是0.75
//添加键值对
persons.put("10000","jack");
persons.put("10001","jim");
persons.put("10002","nanjun");
persons.put("10000","jimin");
//返回键值对个数
//Map中的key无序不可重复 ,如果key重复,map会把原先的value覆盖,这里是“jimin”把“jack”覆盖
System,out.println(persons.side()); //3 因为有两个键值重复了
//判断是否包含指定的key
System.out.println(persons.containKey("10000"));//true
System,out,println(persons.containValue("jimin"));//true
//通过key获02取value
String k="10000";
Object v = persons.get(k);
System.out.println(v);//jimin
//通过key删除键值对
person.remove("10000");
//获取所有的value
Collection vl = persons.values();
Iterator it = vl.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//获取所有key
//如何遍历Map集合 Map集合没有迭代器
Set k = persons.KeySet();
Itertor it2 = K.iterator();
while(it2.hasNext()){
Object id =it2.next();
Object name = persons.get(id);
System.out.println(id+"->"+name);
}
//获取key和value的关系
Set entrySet = persons.entrySet();
Iterator it3 = entrySet.iterator();
while(it3.hasNext()){
System.out.println(it3.next());
}
}
}
//HashMap默认初始化容量是16,默认加载因子是0.75
//Hashtable(接口)默认初始化容量是11,默认加载因子0.75,实现类Properties
//Hashtable中的key和value都是字符串类型
import java.util.Properties;
public class Test{
public static void mian(String[] args){
//1、创建属性类对象
Properties p = new Properties();
//存
//key不能重复,重复value被覆盖
p.setProperty("username","javk");
p.setProperty("password","123456");
//取,通过key获取value
String v1 = p.getProperty("username");
}
}
//集合工具类 Collections java.util.Collections
Collection(不带s)的是接口
使用Collections 可以完成集合的排序,转换等操作
public class Test{
public static void main(String[] args){
List l = new ArrayList();
l.add(1);
l.add(8);
l.add(4);
l.add(9);
//排序
Collections.sort(l);
Iterator it = l.iterator();
whlie(l.hasNext()){
System.out.println(it.next());
}
//Set集合也可以用该方法排序,但是key元素应该是可比较的基本数据类型
//将Set集合转换成List集合
Set s = new HashSet();
s.add(10);
s.add(8);
//转换
List lists = new ArrayList(s);
//排序
Collections.sort(lists);
//将ArrayList集合转换成线程安全的
List li = new ArrayList();
Collections.synchronizedList(li);
}
}
Collections排序方法
```java
//SortedSet 无序不可重复,元素按大小顺序自动排列
public class Test{
public static void main(String[] args){
//创建集合
SortedSet ss = new TreeSet();
// 添加元素(日期类和字符串类也可以按大小排序)
ss.add(1); // 自动装箱,转换成Integer类型
ss.add(4);
ss.add(3);
//遍历
Iterator it = ss.iterator();
while(it.hasNext()){
Object element= it.next();
System.out.println(element);
}
}
}
//当给定的类不可以自动排序时,如何重写java.util.Comparable接口中的compareTo方法
public class Test{
public static void main(String[] args){
SortedSet ss = new TreeSet();
User u1 = new User(1);
User u2 = new User(4);
User u3 = new User(3);
//添加元素
ss.add(u1);
ss.add(u2);
ss.add(u3);
//遍历
Iterator it = ss.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
//由于User类不是基本类,需要重写Comparable接口的compareTo方法
//java程序会自动执行该方法,根据返回值(0,-1和1)进行二叉树排列,我们只需要重写
//需求按照age大小排序
class User implements Comparable{
//String name ; // 如果是比较字符串 ,String类型已经重写了compareTo方法,可以直接
retrun name.compareTo(Object o)
/*
public int compareTo(Object o){ //u1.compareTo(u2)
String s = ((User)o).name;
return name.compareTo(s);
}
*/
int age ;
User(int age){
this.age = age;
}
//重写toString方法
public String toString(){
return "User.age="+age;
}
public int compareTo(Object o){ //u1.compareTo(u2)
int age1=this.age;
int age2 = ((User)o).age;
return age1-age2;
//如果return 0 表示两个值相同,不添加该元素
}
}
//另外一种通过单独编写一个比较器Comparator,实现SortedSet的自动排序(推荐)耦合度比第一种低
// TreeSet(Comparator<? super E>comparator)
//在TreeSet集合创建时,可以加入一个比较器
//Comparator是接口,需要单独编写一个实现类
public class Test{
public static void main(String[] args){
SortedSet products = new TreeSet(new ProductComparator());
Product p1 = new Product(100);
Product p2 = new Product(1100);
Product p3 = new Product(1200);
Product p4 = new Product(1020);
//添加元素
products.add(p1);
products.add(p2);
products.add(p3);
products.add(p4);
//遍历
Iterator it = product.iterator();
while (it.hasNet()){
System.out.println(it.next());
}
}
}
class Product{
int price;
Product(int price){
this.price = price;
}
public String toString(){
return "price:"+price;
}
}
//单独编写一个比较器
//Comparator 接口里面有一个Compare(T o1,T o2)方法,可以重写
class ProductComparator implements Comparator{
public int compare(Object o1,Object o2){
int price1 = ((Product)o1).price;
int price2 = ((Product)o2).price;
return price1-price2;
}
}
//第三种方法使用匿名内部类编写比较器
SortedSet products = new TreeSet(new ProductComparator(){
//单独编写一个比较器
//Comparator 接口里面有一个Compare(T o1,T o2)方法,可以重写
class ProductComparator implements Comparator{
public int compare(Object o1,Object o2){
int price1 = ((Product)o1).price;
int price2 = ((Product)o2).price;
return price1-price2;
}
}
});
泛型
//泛型的优点和缺点
//以下程序没有使用泛型
//缺点:集合元素类型不统一,遍历时候需要做大量类型转换
//优点 不使用泛型的话输出可以不统一
public class Test{
public static void main(String[] args){
//创建一个集合,储存A B C
Set s = new HashSet();
A a = new A();
B b = new B();
C c = new C();
s.add(A);
s.add(B);
s.add(C);
//需求,遍历集合,如果是A调用m1 B调用m2···
Iterator it = s.iterator();
while(it.hasNext()){
Object o = it.next();
o.m1();//Object中没有m1方法,无法执行,只能做3次类型转换
if(o instanceof A){
A a1 = (A)o;
o.m1();
}else if (o instanceof B){
B b1 = B(o);
o.m2();
}else if (o instanceof C){
C c1 = C(o);
}
}
Class A{
public void m1(){
Sys"m1";
}
}
Class B {
public void m2{
Sys"m2";
}
}
Class C{
public void m3(){
sys"m3";
}
}
}
}
泛型的优点和缺点
//优点:统一类型,减少强制转换
//缺点:之能存储一种类型
作用:统一集合中的类型
//泛型语法
public class Test{
public static void main(String[] args){
//创建一个List集合,只能储存字符串类型
List<String> strs = new ArrayList<String>();
//添加元素
strs.add(1); //无法添加
//遍历
//迭代器的类型也变为String
Iterator<String> it= strs.iterator();
while(strs.hasNext()){
String s = it.next();
System.out.println(s);
}
//Map泛型 集合接口、类后面才跟泛型,其他不用加
Map<String,Integer> maps = new HashMap<String,Integer>();
//遍历
Set<string> keys = maps.keySet();
Iterator<String> it = keys.iterator();
while(it.hasNext()){
String k = it.next();
Integer v = maps.get(k);
System.out.println(k+"->"+v)
}
}
}
IO
//IO流
流根据流动的方向可以分为输入流InputStream和输出流OutputStream,流入流出是相对于内存而言,流入内存是输入流,流出内存是输出流,输入又称为“读” 输出流又称为“写”
//流根据读取数据的方式,可以分为字节流和字符流,字节流一次读取一个字节,字符流一次读取一个字符(两个字节),java中一个字符占两个字节
//字节流(万能
)适合读取 声音、视频、图片等二进制文件, 字符流只能读取纯文本文件(word文档不属于纯文本文件,里面有格式),
//java语音中所有的字节流都以Stream结尾,所有的字符流都含有Reader或者Writer ,read是输入,write是输出
java.io.*
FileInputStream //字节流输入
FileOutputStream //字节流输出
FileReader //字符流输入
FileWriter //字符流输出
BufferedReader //带有缓存区的
BufferedWriter
BufferedInputStream
BufferedOutputStream
DataInputStream
DataOutputStream
ObjectInputStream //专门读取java对象
ObjectOutputStream
InputStreamReader //转换流 , 将字节流输入转换为字符流输入
OutputStreamWriter
PrintWriter //标准的字符输出流
PrintStream //标准的字节输出流,默认输出到控制台
java语言中流分为四大家族:(InputStream(抽象类) OutputStream(抽象类) Reader Writer)
//FileInputStream
import java.io.*;
public class Test{
public static void mian(String[] args){
FileInputStream fls = null; //创建一个文件输入流的成员变量(可以在全局使用)
//假设当前目录下有一个名为 test01 的文本文件 , 里面存了“abcdef”
try{
//文件路径
String filepath = "test01"; //相对路径,相对与java文件当前的目录
//String filepath = "绝对路径";//绝对路径表示时“\”要打印成“\\”,也可以改成“/”
fls = new FileInputStream(filepath);
//开始读
int i1 = fls.read();
int i2 = fls.read();
int i3 = fls.read();
int i4 = fls.read();
int i5 = fls.read();
int i6 = fls.read();
int i7 = fls.read();
System.out.println(i1); //97 由于a字符可以用一个字节存储,这里读取一个字节就可以输出97
System.out.println(i2); //98
System.out.println(i3); //98
System.out.println(i4); //100
System.out.println(i5); //101
System.out.println(i6); //102
System.out.println(i7); //-1 当数据读取完后,继续读取,返回-1
//循环读取 这种(上面也是)一个一个字节读取数据,会频繁读写,损害硬盘寿命
while(true){
int temp = fls.read();
if( temp==-1) break;
System.out.println(temp)
}
//可以使用 (int) read(byte[] b) //一次读取多个字节,减少频繁读取对硬盘的损害
从此输入流中最多b.length个字节的数据读入一个byte数组中,返回值是读取的字节数
}catch(FileNotFoundException e){
e.printStackTrace();//针对文件没找到的异常
}catch(IOException e){
e.printStackTrace();//针对读取文件时的异常,io类异常比filenotfound异常宽泛,放在下面
}finally{
/为了保证流一定会被释放,所以在finally执行
if (fls!= null){
try{
fls.close(); //关闭文件流,释放内存
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}
循环读取
FileOutputStream 写
文件复制粘贴
序列化
一、序列化是什么?
序列化就是将一个对象的状态(各个属性量)保存起来,然后在适当的时候再获得。
序列化分为两大部分:序列化和反序列化。
(1)序列化—将数据分解成字节流,以便存储在文件中或在网络上传输。
(2)反序列化—就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的对象实例
可以参考:http://www.cnblogs.com/xdp-gacl/p/3777987.html
一个类只有实现了序列化接口,它的对象才是可序列化的
serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。
类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。
显式地定义serialVersionUID有两种用途:
a. 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
b. 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
- 为什么保存到文件中要序列化?
当我们想把的内存中的对象状态保存到一个文件中或者数据库中时候,Serializable接口能帮我们做些什么?
如上所述,读写对象会有什么问题呢?比如:我要将对象写入一个磁盘文件而后再将其读出来会有什么问题吗?别急,其中一个最大的问题就是对象引用!举个例子来说:假如我有两个类,分别是A和B,B类中含有一个指向A类对象的引用,现在我们对两个类进行实例化{ A a = new A(); B b = new B(); },这时在内存中实际上分配了两个空间,一个存储对象a,一个存储对象b,接下来我们想将它们写入到磁盘的一个文件中去,就在写入文件时出现了问题!因为对象b包含对对象a的引用,所以系统会自动的将a的数据复制一份到b中,这样的话当我们从文件中恢复对象时(也就是重新加载到内存中)时,内存分配了三个空间,而对象a同时在内存中存在两份,想一想后果吧,如果我想修改对象a的数据的话,那不是还要搜索它的每一份拷贝来达到对象数据的一致性,这不是我们所希望的!
以下序列化机制的解决方案:
1.保存到磁盘的所有对象都获得一个序列号(1, 2, 3等等)
2.当要保存一个对象时,先检查该对象是否被保存了。
3.如果以前保存过,只需写入"与已经保存的具有序列号x的对象相同"的标记,否则,保存该对象通过以上的步骤序列化机制解决了对象引用的问题!
在对对象进行实例化的过程中相关注意事项
a)序列化时,只对对象的状态进行保存,而不管对象的方法;
b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
d)并非所有的对象都可以序列化,至于为什么不可以,有很多原因了,比如:
1.安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行RMI传输 等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。
2. 资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配,而且,也是没有必要这样实现。
4、序列化和反序列化代码示例
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化
FileOutputStream fos = new FileOutputStream("object.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Student student1 = new Student("lihao", "wjwlh", "21");
oos.writeObject(student1);
oos.flush();
oos.close();
//反序列化
FileInputStream fis = new FileInputStream("object.out");
ObjectInputStream ois = new ObjectInputStream(fis);
Student student2 = (Student) ois.readObject();
System.out.println(student2.getUserName()+ " " +
student2.getPassword() + " " + student2.getYear());
}
}
public class Student implements Serializable{
private static final long serialVersionUID = -6060343040263809614L;
private String userName;
private String password;
private String year;
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setPassword(String password) {
this.password = password;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public Student(String userName, String password, String year) {
this.userName = userName;
this.password = password;
this.year = year;
}
}
二、反射机制
(1)什么是反射机制
反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息。
(2)反射机制的优点与缺点
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念,静态编译:在编译时确定类型,绑定对象,即通过。动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中,它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该能。
它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
//sun提供的反射机制中的类
java.lang.Class; Class c = xxx
java.lang.reflect.Constructor;
java.lang.reflect.Field; (类的属性)
java.lang.reflect.Method; (类的方法)
java.lang.reflect.Modifier (类的修饰符)
class User{
private String name;
public User(){}
public void m1();
}
反射机制的作用
: 1 反编辑 : .class->.java
2 通过反射机制访问java类的属性,方法和构造方法
----------------------------------------------------------
//获取Class类型对象的三种方式
public class Test{
public static void main(String[] args){
//1 static Class forName(String classname) 返回指定类名的Class对象
Class c1 = Class.forName("Employee");//c1引用保存内存地址指向堆中对象,该对象代表的是 //Employee整个类
//需要处理ClassNotFoundException
//必须用类的全名(带上包名
//2java中每个类都有.class属性
Class c2 = Employee.class;
//3java语言中对象都有getClass方法
Employee e = new Employee();
Class c3 = e.getClass
//这里c1和c2 c3指向都是Employee类(不是对象),jvm中只有一个Employee类,所有他们内存地址相同
}
}
----------------
//该类编译生成.class文件后,就可以利用反射机制去操作这个类
public class Employee{
private String name;
//Constructor
public Employee(){}
public Employee(String name){
this.name= name;
}
//方法
public void work(){
System.out.println("work");
}
}
public class Test{
public static void main(String[] args) throws Exception{
//这是将A.class文件装载到jvm的过程,静态语句块会执行
Class.forName("A"); // ddd
Class c = A.calss // 静态语句块不会执行
A a =new A(); //ddd
}
}
Class A{
static{
//静态语句块会在类加载的时候执行
System.out.println("ddd");
}
}
---------------------------------------------------
//通过Class类对象创建java对象
public class Test{
public static void main(String[] args) throws Exception {
Class c= Class.forName("Employee");
//创建此Class对象所表示的类的实例
Object o =c.newInstance(); //这里创建了一个Employee的实例,并且是通过调用无参构造方法来创建的
//例子:通过反射机制输出系统当前时间
Class c1 = Class.forName("java.util.Date");
Object o = c1.newInstance();
if(o instanceof Date){
Date t = (Date)o;
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(t));
}
}
}
public class Employee{
private String name;
//Constructor
public Employee(){System.out.println("无参构造方法")}
public Employee(String name){
this.name= name;
}
//方法
public void work(){
System.out.println("work");
}
}
多线程
https://www.cnblogs.com/yjboke/p/8911220.html
线程栈
主线程栈和子线程栈是分开的