java开发的环境
1.jdk–>java开发工具包(开发人员需要安装jdk)
a.jre–>java运行环境(开发人员已经开发出来的程序所需要的环境)
(1)jvm:java虚拟机,
(2)类库:程序所需要的资源库
b.开发工具
javac.exe,javadoc.exe ,java.exe…
2.jdk安装(略)
安装路径不要带中文(原因:在打开程序时,解析路径有可能会出现乱码)
3.配置环境变量(方便使用命令而设置的环境变量)
打开:
计算机–>属性–>高级系统设置–>高级–>环境变量–>用户变量
找到path属性或者新建path属性,在path的原有值后添加分号";"和安装jdk的所有命令所在的目录的全路径
ex:
…;D:\MyWork\jdk1.8.0_152\bin
==注意:==分号“;”一定是要英文的分号,路径尽量不要有中文。
JAVA_HOME
CLASSPATH 这两个变量可以不用配置(视情况而定)
4.检查是否配置成功
快捷键:win+r 输入cmd
检查两个命令:
java -version
javac
入门程序的编写
1. 在D:下创建java目录,day01子目录
2.在day01下创建 java源文件
HelloWorld.java
3. 编写程序
关键字 类型名
public class HelloWorld{
//main方法,程序的入口
public static void main(String args[]){
//输出语句:
System.out.println("Hello World");
}
}
程序的运行
-
编译程序
将java源文件通过编译工具(javac),转成 java字节码文件(class文件)输入格式:
javac XXXX.java -
运行程序
将java字节码文件,在jvm上运行起来输入格式:
java XXXX.class注意:.class 扩展名 一定不要写
java注释,用来解释代码,javac会忽略注释内容
1. 文档注释
/** 内容 */
2. 多行注释
/* 内容 */
3. 单行注释
// 内容
变量
1.概念
是jvm在内存中开辟的一个存储空间,用来存储数据的。
通过变量名来区分和操纵这些空间。
2.变量名的命名规则
(1)由字母,数字,下划线和$组成,数字不能开头
(2)能使用中文,但是不建议使用
(3)不能使用java关键字(保留字)
(4)见名知意,驼峰命名法
3.变量的使用规则
(1)先声明(定义),再初始化
(2)在声明时,必须规定类型(java语言是强类型的语言)
(3)变量有自己的作用域。
作用域:从声明处开始,到所在的花括号的结束符合为止
(4)变量可以多次进行存和取操作
基本数据类型 (四类八种)
1.整数类型(4种)
数据类型 | 字节 | 位数bit | 范围 |
---|---|---|---|
byte(字节类型) | 1 | 8 | -27~27-1 |
short(短整形) | 2 | 16 | -215~215-1 |
int (整型) | 4 | 32 | -231~231-1 |
long (长整形) | 8 | 64 | -263~263-1 |
注意1:直接写的整数,我们称之字面量(字面值),默认是32bit的int类型。long的字面量,需要添加L/l
注意2:两个数字进行运算,会先转成同类型的数据,再进行运算。转成的类型,至少是int级别的数据
2.浮点数类型(2种)
数据类型 | 字节 | 位数bit | 范围 |
---|---|---|---|
float(单精度类型) | 4 | 32 | -231~231-1 |
double(双精度类型) | 8 | 64 | -263~263-1 |
注意1:单精度浮点型,精度是7位(一共7位,包括整数部分和小数部分)
注意2:双精度浮点型,精度是15(一共15位,包括整数部分和小数部分)
3.字符类型(1种)
char(字符类型,无符号整数) 16bit 2个字节
范围:0~65535
赋值字符时,必须使用单引号,要么是二进制对应的10进制
‘A’‘Z’:6590
‘a’‘z’:97122
‘0’‘9’:4857
特殊字符:
‘\t’
‘\n’
‘\r’
‘\b’
编码集:每一种编码集有自己的一套对应规则
一个底层的二进制对应一个字符
UTF-8
GBK
UNICODE
4.布尔类型(1种)
boolean:只有两个值,true,和false,用于判断条件成立与否。
类型转换:
1.自动转换(隐式转换)
byte --> short --> int -->long -->float -->double
char -->
小贴士:小范围类型的数据会自动转换成大范围类型的数据,会自动在二进制前面添加相应位数的0或1。
2.强制转换(显式转换)
double–>float–>long–>int–>short–>byte
char–>
大范围类型的数据转成小范围类型的数据时,需要强制转换
转换语法:
小范围类型 变量名 = (小范围类型)大范围变量;
int a = 10;
byte b = (byte)a;
运算符号
原则1:任何两种类型的变量或字面值做运算,都会先自动转成较大类型的数据,再计算
原则2:封闭式运算
1、+,-,*,/, ++,–
int max = 2147483647;
int min = max +1;//min=-2147483648
max = min -1;//max=2147483647
自增自减运算符:++,–
(1)情况1:单独使用时,变量本身会自动+1或-1;
a++;
++b;
c–;
--d;
(2)情况2:表达式参与其他运算时
int a = 1;
int b = (a++);
或
int c = (++a);
或
int d = (a–);
或
int e = (–a);
符号在前,变量先运算,再把新值赋值给表达式,
符号在后,先赋值给表达式,变量本身在运算
方法
1.概念,
方法就是一个代码片段的封装,可以完成某一项功能,也可以称之为函数。
==注意:==一个方法尽可能只有一个功能。
2.方法的定义格式
修饰词 返回值类型 方法名(形式参数列表){
方法体
}
3.访问权限控制修饰词
3.1.四个修饰词
public
protected
default
private
3.2.修饰类型
a. 文件内的普通类型(外部类):
只能使用public和默认的
b. 内部类:
四个修饰词都可以修饰内部类
3.3.修饰属性(字段,成员变量)和方法
4.返回值类型
(a) void :无返回值类型
(b) java语言中的任意类型:八大基本数据类型,引用类型
5.return关键字
含义:结束,返回的含义。
(a)void方法:
可以使用return关键字,来结束方法,但是return后直接是分号";",没有任何数据也可以不用return.
(b)返回值类型为java的任意类型
必须有关键字return,而且return关键字后必须要跟有返回值类型中的某一个数据或变量
6.方法名
尽量做到见名知义,驼峰命名法。首字母小写。
7.形式参数列表。
格式:变量的声明格式
reg: (int a, int b)
(int a,long b,String[] c)
优势:有形式参数的方法,比较灵活,可以通过传入不同的实际参数,来运行不同的计算。没有形式参数的方法,数据不够灵活。程序是死的。
8.方法体
就是代码片段
reg1: 封装一个方法sum(),用于计算8与10的和
public static void sum1(){
int a = 8;
int b = 10;
System.out.println(a+b);
}
public static void sum2(int a,int b){
int c = a + b;
System.out.println(c);
}
public static int sum3(){
int a = 8;
int b = 10;
return a+b;
}
public static int sum4(int a,int b){
int c = a + b;
return c;
}
9.方法的调用
思想小技巧:定义出来的一个方法,就是固定了程序的逻辑流程
a.void方法的调用
在调用处:直接写方法名,有参传参(方法有形参,调用时就传实参)
public static void main(String[] args){
sum1();
}
public static void main(String[] args){
int c = 100;
int d = 200;
sum2(c,d); //有形参传实参
}
b.有返回值类型的方法调用
在调用处:使用返回值类型,声明变量用等号来接受方法的返回值
public static void main(String[] args){
int s = sum3();
System.out.println("s:"+s);
}
public static void main(String[] args){
int a1 = (int)(Math.random()*100);
int b1 = (int)(Math.random()*100);
int s = sum4(a1,b1);
System.out.println("s:"+s);
}
OOP(面向对象编程 ooa,ood)
1.面向对象思想
使用人类的自然思维,从现实生活中来映射程序设计,使用类,对象,封装,继承,消息等来设计程序
程序的基本单位:类,整个程序是由多个类组成。
面向过程思想:自顶而下,逐步细分,模块化。
基本单位:函数。将一个程序 逐步细分成最终的多个函数。
粒度:组装一台电脑
eg:
面向对象的粒度大:
主板,内存条,cpu等等;粒度大,难度小
面向函数的粒度小:
电路板,各种二极管等等零件; 粒度小,难度大
2.类
2.1.抽象数据类型
使用一堆不同类型的数据来描述一种新的事物,这种事物是抽象的。
//使用studentid,className,name,age,teacherName,school,这些数据就可以描述一种新的类型,
int studentid
String className
String name;
int age;
String teacherName
String school
//可以使用方法来描述这类事物的行为。描述学生的行为
goToSchool(String tool);
homeWork(String type);
playGame();
2.2.类的定义。
将抽象数据类型的共同特征和共同行为封装起来,定义成类体,并赋予一个类名
public class Student{
//共同特征,也叫属性,成员变量,全局变量,字段,FieldName
int studentid;
String className;
String name;
int age;
String teacherName;
String school;
//共同行为,也叫方法method,函数。类对象的行为,一般情况下都是非静态的。
public void goToSchool(String tool)
public void homeWork(String type);
public void playGame();
}
3.对象(Object)
在计算机中,通过类来构造一个对象实例,当给属性赋值(初始化)后,这个对象就变的有意义了。在使用对象时,一般都是使用对象的属性或者是方法。
通过new关键字,来构造对象实例。
格式:
new Student(); //这种写法,jvm就会在内存中开辟相应的存储空间,存储对象的属性。
在构造对象后,一般都会将此对象在内存的位置存储在变量中,如果地址没有被任何变量存储(引用),会成为垃圾,需要及时清理这样的垃圾,否则容易造成内存溢出。
正确的写法:
Student s = new Student();//赋值操作,类型必须匹配。否则,编译器不通过。
reg:
Student s1 = new Student();//这是构造对象
//通过"变量名."来调用对象的属性或方法
s1.studentId = 1001;
s1.className = “计算机1班”;
s1.name = “zhangsan”;
s1.goToSchool(“自行车”);
==总结:==类是对象的模板,对象是类的具体实例。创建对象也称之为对象实例化。
4.引用变量
a.除了基本数据类型外,所有的类型都是引用类型,如String,Scanner,自定义的Student,Teacher等等
b.创建出来的对象存储在内存的堆中,我们要将内存中的地址值存储在变量中,换句话说,通过变量里的地址值可以找到内存中的对象,这样的变量我们称之为引用变量,简称引用。
==总结:==通过引用(引用变量)来找到对象
5.null和NullPointerException
a.null
null是空的意思,是引用类型的默认值,
reg1:
String[] arr = new String[5];//其实最初有5个null.
reg2:
Student s = new Student();//在内存中开辟空间存储Student对象
分析:主要是给属性开辟存储空间,此时存储空间里存储的是默认值
因此"new Student()"对象的属性默认值:
studentId=0
className=null
name=null
reg3:
Student s = null; //引用变量s里存储的是null,而非某一个对象的地址值,即s不指向任何对象。
释放内存操作:
Student s = new Student();
…
s = null;//清空s中的地址值。
b.NullPointerException(空指针异常)
reg:
Student s = null;
s.studentId = 1001;
s.name = “zhangsan”;
相当于
null.studentId
null.name
null.goToSchool(“自行车”);
==注意:==javac命令(编译器)检查不出来这种异常,当程序运行时,才会出现异常。我们称之为运行时异常。ArrayIndexOutOfBoundsException.
6.向上造型
父类型变量引用子类型对象
class Person{
private String name;
private int age;
}
class Student extends Person{
private int sno;//学号
private double score;//成绩
}
main(){
Person p = new Student();//向上造型。
//此时,p变量只能调用出 Person类型的中属性和方法
//要注意的是,方法是否重写,如果重写,执行的是对象类型中的逻辑
}
7.向下转型
当向上造型的情况满足不了我们的需求时,需要向下转型
reg:
Person p = new Student();//向上造型。
需求是:查看对象的学号sno。而p变量调不出sno,就需要向下转型
格式: 子类型名 变量名 = (子类型名)父类型变量
reg:
Student s = (Student)p;//这时,就可以使用s.sno了。
注意:在向下转型时,有可能会出现运行时异常:ClassCastException。如果出现了此异常,没有及时处理,就会终止程序。为了避免这种情况发生,在转型前都会进行判断是否属于这种类型
if(p instanceof Student){//避免了类造型异常
Student s = (Student)p;
}
this关键字
非静态方法中,隐藏着一个this引用变量,这个变量指向将要创建的对象。可以在方法内显式引用对象的属性或方法。
内存管理机制
程序在运行期间,JVM对其所管理的内存分成五部分。
1.方法区:
是多个线程共享区间,主要用来加载存储类,方法(字节码文件)这样的信息,还有运行时常量池。
注意:jdk1.7以后,运行时常量池移至到堆中
2.堆:
也是共享区间,主要用于存储new关键字创建的对象。
3.栈:
主要作用是用来存储栈帧。栈帧是jvm为运行到的每一个方法单独开辟出来的存储空间,存储
这个方法中的所有局部变量。当方法执行结束后,jvm会释放栈帧。
4.PC寄存器:
主要用来记录每个线程执行到位置(阻塞,挂起)
5.本地方法栈(native)
主要存储本地方法信息(平台内置的方法:native方法)
方法的重载(OVERLOAD)
a.概念
在一个类体中,方法名相同,形参列表不同的方法共同存在,这样的方法称之为方法重载
reg: 方法签名
distance() : distance
distance(Point p) distance + Point
distance(Point p1,Point p2) distance + Point,Point
上述三个方法是重载关系
方法名+形参类型列表 = 方法签名
reg:
sum(int a) sum + int
sum(int a,int b) sum + int,int
sum(double a) sum + double
sum(double a,int b) sum + double,int
sum(int c,int d) sum + int,int
注意:第五个方法不可以同上述四个方法在同一个类体中,因为第五个方法的方法签名与
第二个方法的方法签名相同。违反了:一个类体不能存在有相同方法签名的方法。
构造方法(构造器)
a.概念
类体中特殊的方法,特殊在没有返回值类型这个位置;方法名还与类名相同。
格式:
修饰词 类名(形参列表){
逻辑体
}
b.作用
用来初始化对象的成员变量(属性、字段)。
小贴士:new 关键字 就是用来调用类的构造方法的,负责实例化对象,构造方法负责初始化对象。
c.默认构造器
当在定义引用类型时,系统会默认提供一个无参构造器。即:
public 类名(){
}
小贴士:当程序员手动提供构造器时,系统将不在默认提供构造器.
d.构造器也可以重载
类体中也可以有多个构造器,方法名相同,形参类型列表不同
e.this关键字的另外一种用法
在构造器中,可以通过this(有参传参)这种写法,调用本类中的其他构造器。
继承
1.思想
java语言中,在设计类型时,如果多种类型有相同的特征或者是相同的行为时,我们可以将这些相同的内容,抽取出来,单独封装成一种类型,这种类型我们称之为父类(超类、基类)然后使用关键字extends,来继承这个父类的内容,而这些类型成为子类型。
reg:
public class Super{}
public class Sub1 extends Super{}
public class Sub2 extends Super{}
public class Sub3 extends Super{}
public class Sub4 extends Super{}
Super是父类,Sub1,2,3,4是子类型。这些子类型都有共同的一些特征,当然在设计子类型时,可以进行扩展自己独有的属性和方法。
2.继承的优点
a.可以减少代码量,提高工作效率
b.有利于二次开发,扩展功能
3.继承的缺点
a.程序的耦合度高
b.代码可靠性差
4.java语言只支持单继承。
一个类只能有一个父类,但是一个类可以有多个子类
父类中的属性与子类中的属性特点
a.父类中的私有属性,子类继承了,但是不可见,可以通过其他方法来获取
b.子类中可以扩展自己的属性,属性名可以和父类中同名,但是不建议这么做
class Super{
private String name;
}
class Sub extends Super{
private String name; //此时,Sub对象在内存中会开辟两个name空间
//一个是super.name
//一个是this.name
}
==注意:==如果同名,无疑是增加辨识难度。为何不起个别的名。
方法的重写(OVERRIDE)
1.子类可以重写父类的非私有方法
2.方法签名不变:方法名不变,参数类型列表也不能变,只修改逻辑体
3.返回值类型是可以变的:
子类中重写方法的返回值类型可以与父类中方法的返回值相同;
也可以是父类方法的返回值类型的子类型
class Super{
protected Object method1(){}
}
class Sub1 extends Super{
public String method1(){//返回值类型String 是Object的子类型,OK
//修饰词的范围可以变大 OK
}
}
class Sub2 extends Super{
private int method1(){//返回值类型int 与Object没有父子关系,不Ok
//修饰词变小了,不OK
}
}
4.修饰词是可以改变的
a.父类中的私有方法,子类继承了,但是不可见,即不能用。
b.其他修饰词,在重写过程中,可以不变。
c.要改变的话,需要
5.父类中final修饰的方法,不能被重写
6.final修饰的class,不能被继承
构造方法
a.构造方法不能被继承,可以被子类调用。
b.子类调用父类构造方法:super(有参传参) (和调用本类构造方法:this(有参传参)做一下对比)
多态(面向对象三大特征:继承,封装,多态)
1.针对于对象来说:
某一个对象处于不同的环境下,有不同的状态和功能
比如张三这个具体Person对象,
在学校这种环境:
张三是一名老师。
可以有属性teachId
可以teach()
可以work()在家里这种环境:
张三是一个丈夫
可以有属性id
可以cook()
可以sweep()
可以照顾孩子()
interface Teacher{
void work();
void teach();
}
interface Family{
void cook();
void sweep();
}
class Person implements Teacher,Family{
void work();
void teach();
void cook();
void sweep();
}
public static void main(String[] args){
Person p = new Person("zhangsan");
Family f = p;
f.sweep();
f.cook();
Teacher t = p;
t.teach();
t.work();
}
小贴士:不同的父类型的引用可以指向同一个对象,有不同的功能。
2.针对于变量来说:
class Animal{
void eat();
void noise();
}
class Fish extends Animal{
void eat(){
System.out.println("鱼吃鱼食");
}
void noise(){
System.out.println("鱼叫");
}
}
class Cat extends Animal{
void eat(){
System.out.println("猫吃鱼");
}
void noise(){
System.out.println("喵喵喵");
}
}
public static void main(String[] args){
Animal a = new Cat();
run(a)
}
run(Animal a){
a.eat();
}
小贴士:同一个变量指向不同子类型的对象时,同一个方法有不同的逻辑实现。
3.引申
a.方法被调用时,主要看具体对象
Animal a= new Cat();
a.eat(); //a是Animal类型的变量,但是方法eat要看变量具体执行的对象类型
a.name; //name一定是Animal类型中的。
访问权限控制修饰词
1.四个修饰词
public
protected
default
private
2.修饰类型
a. 文件内的普通类型(外部类):
只能使用public和默认的
b. 内部类:
四个修饰词都可以修饰内部类
3.修饰属性(字段,成员变量)和方法
static修饰词
1.修饰属性
class Student{
static int count=100;
}
a.static修饰的属性不是对象的,而是类的,一般情况下用类名来调用
b.static修饰的属性存在方法区中,只有一份,堆中的对象共享这个属性
c.static修饰的属性不要在构造器中赋值,一般情况下都是在定义期间赋值
2.修饰方法
a.static修饰的方法不是对象,而是类的,一般情况下用类名去调用
b.static修饰的方法只与传入的数据有关,与对象无任何关系
c.static修饰的方法一般都用来定义"工厂","工具"等方法
Arrays.copyOf(arr,arr.length+1)//工具方法
Calender.getInstance();//工厂方法
d.static修饰的方法存在方法区中,只加载一次。在此方法中,没有隐藏的this关键字,因此不能直接访问非静态成员变量
3.修饰类
a.static只能修饰内部类,我们称之为静态内部类
b.不能直接访问外部类的成员
4.静态块
a.static可以修饰类体中的代码块:static{}
b.静态块的执行时机:
在类加载期间执行代码块中的逻辑,只执行一次
c.一般用于加载静态资源:如图片,音频,视频等等。
final修饰词
1.修饰类
a.final修饰的类不能被继承。通常用于避免继承泛滥。
2.修饰属性
a.final修饰的属性,只能初始化,不能第二次赋值。
b.final修饰的属性,要么直接初始化,要么在构造器中初始化,不能在其他方法中初始化
3.修饰方法内的局部变量
a. 初始化的时机,只需要在使用前进行即可。当然不能第二个赋值。
4.修饰方法
a.final修饰的方法,不能被重写。通常用于避免子类不小心重写此方法。
常量
a. 不能被更改的,常用的变量,我们称之为常量
如:π,Math.PI();
自定义:char minus_sign = ‘-’;//把minus_sign变量当成常量用,那么就不可以被更换
b. 常量通常用final + static 来修饰。
c. 至于四个权限访问修饰词要使用哪个,完全看需求。
抽象类
在继承思想中,子类的功能逻辑更加具体化,父类的功能更加普通(普遍)化。如果,很多子类的方法都需要重新来设计(OVERRIDE)时,那么父类中的方法的逻辑就不需要了,因此父类的这种方法无需提供方法体(提供了也要被覆盖,多此一举)。所以这样的方法我们可以使用abstract来修饰,不需要提供{}体逻辑(),需要使用分号";"来结束,这样的方法称之为抽象方法。此时,class也必须使用abstract修饰,因此被称为抽象类。
a.抽象类class前一定要使用abstract关键字修饰
b.抽象类不能实例化,因为没有任何意义
c.抽象类中可以提供构造器,
d.抽象类中可以没有抽象方法,有抽象方法的类一定是抽象类
e.抽象类不能使用final修饰。
接口
因为java只支持单继承的思想,所以,有的时候,我们需要一个类来实现多个类型的功能。
class A{
teach();
}
class B{
cook();
}
class C{
}
我们的需求是:c又有A的功能,又有B的功能,但是extends满足不了我们的需求。因此我们设计了一种接口的思想:一个类可以实现多个接口,来达到多继承的目的。
1.接口的关键字:inteface
定义格式:
interface interName{
}
2.接口的内容
a.可以有属性
但是都是常量,也就是说:属性都是pubic static final来修饰的。
修饰词不写,系统会默认提供。必须直接初始化。
b.可以有方法
都是抽象方法。没有{},必须使用分号";"结束。
默认使用public abstract修饰
3.接口不能实例化,没有任何意义
4.接口不能提供构造器
5.接口是一种规范。
6.接口被实现的关键字
implements,一个子类可以实现多个接口
interface A{}
interface B{}
class C implements A,B{
}
7.接口与接口之间是继承关系
interface A{}
interface B extends A{}
内部类
1.成员内部类
public class Outer {
private int age;
private String name;
Inner inl;//在外部类中提供内部类的成员变量
protected class Inner{
public void show(){
System.out.println(age);
}
}
public void run(){
//在外部类中的方法中使用内部类
Outer o = new Outer();
Inner in = o.new Inner();
}
}
2.匿名内部类
在定义某一个类型的子类时,如果只需要在方法中使用一次,那么就没有必要定义子类的具体内容,无需赋与子类名称。
这样,就可以使用匿名内部类的方式:
InterA ia = new InterA(){
//方法的重写
};
说明:我们使用多态的写法,使用接口InterA声明变量指向InterA的子类型对象。
3.静态内部类
成员内部类加上static修饰。
4.方法内部类
是在方法的内部定义的类型
Object
1.概述
- Object是java语言中继承结构中的最顶点
- 自定义的类型如果不显式继承Object,那么系统会默认其父类为Object
- Object是所有引用类型的直接父类,或者是间接父类
2.Object提供的方法
1、hashCode()
Object类型中提供了此方法,用于生成类型中的实例化对象的一个hash值。默认调用的是本地平台的计算方法
注意:在自定义类型时,一般都重写此方法
2、equals()
源码:
public boolean equals(Object obj) {
return (this == obj);
}
Object提供的equals方法,用于比较两个对象是否为同一个对象。如果返回true,说明是同一个对象,如果返回false,说明不是同一个对象。
注意:Object提供的此方法,用途比较窄,程序员一般需要重写此方法,是其有广泛的含义
3、clone()
Object提供此方法,用于对象的克隆,可以称之为副本。
4、toString()
源码:
public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
}
说明:Object中提供此方法,用于返回对象在内存中的地址描述信息,描述信息格式为:“类全名”+"@"+对象的hash值的16进制的字符串形式
注意:程序员在自定义类时,一般也要重写此方法,使之返回对象的属性值的字符型形式,用于描述对象信息,而不是地址信息。
5、notify/notifyAll()
说明:Object中,提供这两个方法,主要用于线程锁机制,通知处于等待的线程,使处于等待的线程进入可执行状态。
6、wait()/wait(long times)
说明:提供此方法,主要用于线程锁机制,可以让正在运行的线程处于等待状态。目的是让下一个时间片段让给其他的线程。(参考并发原理)。
JavaBean规范
java程序员默认遵守的编程规范
1、所有的属性都要是private权限修饰
2、属性要求有get/set方法,如下所示:
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
说明:Bean是豆子的含义,get/set方法名上的与属性名相同的内容被称之为Bean
3、提供至少两个构造器
第一个:无参构造器
方便第三方框架自动创建对象。
第二个:全参构造器
4、重写equals方法
说明:为了使equals方法变得更有意义,我们要重写此方法,用于比较this对象和obj对象的属性。
源码:
public boolean equals(Object obj) {
return (this == obj);
}
重写规则:
(1)首先判断传进来的对象是不是this
if(this == obj){
return true;
}
(2)再判断传进来的对象是不是null
if(obj == null){
return false;
}
(3)再判断传进来的对象是不是同类型,不是同类型,直接返回false;是同类型,再一一比较相同属性的值。
if(obj instanceof ClassName){
ClassName other = (ClassName)obj;
//再一一比较属性的值是否相同,只要有一个不同,就返回false
}
说明:instanceof关键字的作用是判断某一个对象是否属于某一类型,返回值为boolean
格式:
boolean flag = 变量名 instanceof 类型名
小贴士:
判断两个对象的类型是否相同,可以使用下列代码
boolean flag = this.getClass() == obj.getClass() 变量名.getClass会获取本类型的Class对象,而同一类对象的Class对象在内存中是唯一的,有且只有一 个。
强调: "“的意义
引用类型的两个变量,如果使用”"进行比较,比较的是引用变量中的地址信息是否相同,换句话说,就是比较是不是同一个对象。
5、重写hashCode方法
如果重写了equals方法,一般都有必要重写hashCode方法。(在集合知识点中重要讲解)
6、重写toString方法
程序员在自定义类中,一般都会重写此方法。用于返回对象属性值的字符串表现形式。也就是对象的描 述信息,而非地址信息。
重写格式:类全名[属性名=属性值,属性名=属性值]
小贴士:一般情况下,在使用变量时,都会默认调用变量的toString()方法
(1)做字符串拼接时
a+"," // 系统会默认调用a.toString()
(2)做输出时,
System.out.println(a); //会默认调用a.toString()
异常Exception
在java编程语言结构中,会有不正常的情况发生,这种不正常的情况分为两大类型一种是error类型,这种情况,一般都是jvm出现了问题。不是程序员能解决的范围另外一种是Exception,这种情况,一般是程序出现了问题,如除数为0,数组下标越界等等,程序员可以自行解决。(error和exception的父类为throwable)
1.Exception的分类
- 检查型异常(编译期异常):编译器(javac)在编译期间会进行语法的检查,一些简单的运算,异常的检查工作 IOException及其子类都是检查性异常。
- 非检查型异常(运行时异常):编译器在编译期间检查不出来的,只能在运行期间才会出现的异常。
RuntimeException及其子类都是非检查型异常。
2.异常的出现情况
异常在编译期间出现是比较合适的,因为编译期间出现异常,就不会生成class文件。这样程序员就可以提前进行异常的解决。但是有些异常不会再编译期间出现,这时,对于程序员来说,解决起来就困难了很多。java语言提供了异常的处理机制,来解决这样的情况。
3.异常抓捕机制
语法:
try{
}catch(Exception e){
}
- jvm的运行机制在运行程序时,如果出现了异常,那么就会创建异常对象,用来 封装异常信息,然后进行抛出(throw)。
- 运行机制会把异常对象,抛给代码片段所在的方法内部,由此方法来进行处理,如果此方法不处理,会继续抛给调用此方法的方法,由调用者来处理,这个过程我们称之为抓捕(catch)过程。
- 如果最后一个方法,如main方法,也没有处理此异常,那么就抛给了jvm。jvm会终止程序的运行
4.抓捕机制的使用
a.将可能出现异常的代码片段,放进try{}后的花括号内
try{
String str = "123abc";
int num = Integer.parseInt(str);
System.out.println(num);
}
说明:如果在try中出现了异常,那么java运行机制,就会抛出异常信息,封装成异常对象
b.抓捕异常(catch)
try{
String str = "123abc";
int num = Integer.parseInt(str);
System.out.println(num);
}catch(Exception e){
}
说明:catch就会将异常抓捕,异常对象的引用就会传递给e变量,然后再由catch部分的花括号内的逻辑来处理异常。
c.处理情况
比如进行异常信息的提示,处理,或不解决,可以由调用此方法的调用者解决(throw)
建议:尽可能的在catch部分中解决异常。
5.finally
- finally为异常处理机制提供了一个统一的出口。通常用于资源的释放,删除临时文件,关闭IO流等操作
- finally是可选的
try{
}catch(Exception e){
}finally{
//这部分可选
}
- 如果有finally这部分,那么不管try中是否出现异常,都会执行finally部分里的逻辑。
6.多catch情况
代码片段中,出现的异常类型不一定是哪一种,因此我们可以使用多个catch来抓捕不同类型的异常
写法:
try{
}catch(ArrayIndexOutOfBoundsException e1){
System.out.println("数组下标越界");
}catch(ClassCastException e2){
System.out.println(e.getMessage());
System.out.println("类造型越界");
}
多catch中异常处理顺序:
从上到下catch处理异常的顺序为:没有父子的异常类型可以随便。但是如果有父子关系,那么应该先处理子类型异常对象,再处理父类型异常对象
} catch (ArrayIndexOutOfBoundsException e1) {
System.out.println("数组下标越界");
} catch (ClassCaseException e2) {
System.out.println(e2.getMessage());
System.out.println("类造型越界");
} catch (NullPointerException e3){
e3.printStackTrace();
System.out.println("空指针异常");
}
说明:上述多catch中的异常类型,没有父子关系,先写谁都可以
} catch (ArrayIndexOutOfBoundsException e1) {
System.out.println("数组下标越界");
} catch (Exception e2) {
System.out.println(e2.getMessage());
System.out.println("类造型越界");
}
说明:上述多catch中的异常类型,有父子关系,所以要先写子类型异常对象,在写父类型异常对象
7.throw关键字
throw 用于抛出异常,不进行处理;在catch模块中,如果不想处理异常,想抛给调用此方法的调用者处理,那么就需要使用此关键字;throw可以抛出运行机制抛出的异常,也可以抛出程序员自定义的异常。
catch(Exception e){
// throw e;// 抓捕到的异常,抛给调用者,由调用者统一进行处理。
throw new RuntimeException("出现了运行时异常,除数为0");
}
8.throws关键字
当程序员在定义方法期间,不进行代码片段的异常机制处理,而是声明此方法中可能出现的异常,由调用此方法的调用者处理,需要在方法上使用throws关键字,表示声明,通知的含义。
用法:
public int chuFa(int a,int b) throws FileNotFoundException{
FileInputStream fis = new FileInputStream("D:/java/day10/demo.txt");
}
说明:在定义方法上,即小括号后 throws
包装类
java语言是面向对象的语言,而八大基本数据类型的数据不是对象,而我们有的时候,有特殊的需求,需要把基本数据类型的数据当做对象来研究,因此包装类的设计就是要完成这样的需求的。
基本数据类型 | 包装类型 | 父类 |
---|---|---|
byte | java.lang.Byte | java.lang.Number |
short | java.lang.Short | java.lang.Number |
int | java.lang.Integer | java.lang.Number |
long | java.lang.Long | java.lang.Number |
float | java.lang.Float | java.lang.Number |
double | java.lang.Double | java.lang.Number |
char | java.lang.Chracter | java.lang.Object |
boolean | java.lang.Boolean | java.lang.Object |
1.Number类型
是一个抽象类型,提供了几个抽象方法,如:
int intValue():返回指定Number对象的int类型的一个值
long longValue():返回指定Number对象的long类型的一个值
double doubleValue():返回指定Number对象的double类型的一个值
2.Integer类型
说明:六个Number的子类型选择Integer进行讲解,其他五个用法原理都是一样的。
构造器:
public Integer(int value) {
this.value = value;
}
private final int value;
说明:此构造器是将形参的值赋值给Integer类型中封装的final修饰int类型的value属性。因此,我们可以知道Integer对象一旦创建,里面封装的值不可以更改。
小贴士:Integer等这六个子类型都是final修饰的类型,因此也不能有子类型了。
Integer类型提供的常用常量
static final int MIN_VALUE
static final int MAX_VALUE
说明:类型中提供了相应的常量如MAX_VALUE、MIN_VALUE等来存储对应的基本数据类型的最大值和最小值。
reg:
System.out.println(Integer.MAX_VALUE);//就可以获取int类型的最大值
System.out.println(Long.MAX_VALUE);//就可以获取long类型的最大值
Integer类型提供的常用方法
static int parseInt(String s)
说明:Integer内提供此方法,目的是将指定的字符串转成相应的int类型的值
static Integer valueOf(String s)
说明:将指定的字符串转成相应的Integer类型对象
static Integer valueOf(int i)
说明:将指定的int类型的值转成相应的Integer类型对象。此方法也可以称之为包装方法,对应的int intValue()就是拆包的方法。
3.包装(装箱)和拆包(拆箱)
3.1、装箱,变成Integer对象
定义一个Integer对象,封装int类型的10,这个过程叫做包装
第一种:使用构造器
Integer i = new Integer(10);
System.out.println(i);
第二种:使用Integer的静态的包装方法
Integer in =Integer.valueOf(100);
System.out.println(in.getClass()==i.getClass());
3.2、拆箱:由Integer变成int
拆箱操作
Integer i1 = new Integer(8);//装箱
使用拆箱方法
int value = i1.intValue();
System.out.println("value:"+value);
3.3、aotuboxing(自动拆装箱操作)
在jdk1.5版本之后,提供了包装类的自动拆装箱操作。
(1)自动装箱
Integer i = 10;//在编译期间,编译器会自动调用装箱方法,转成包装类型
(2)自动拆箱
Integer i = new Integer(100);//i指向的是一个包装类对象
int v = i; //自动拆箱,在编译期间,编译器会自动调用拆箱方法,转出基本数据类型的值
4.常量池
在使用基本数据类型的 -128~127之间的任何数据进行装箱操作时,jvm会维护一个缓冲区间。当数据装箱后,会先去此缓冲中查找是否有相同值的对象,如果有,就让变量指向缓冲区内的这个对象。如果没有,就将装箱后的对象移至此缓冲区中,然后让变量指向这个对象。在缓冲区中,相同数值的包装类对象,有且只有一个。
小贴士:不论是包装类常量池还是字符串常量池,原理是一样的,目的是为了减少内存开销。
Date
概述:java.util.Date是java语言提供的一个标准时间类型,其提供了一些计算时间分量的方法。但是,多数方法已经被Calendar所取代
Date类型提供的时间表示方式为距离某一特定时间点的毫秒数来表示一个时间点。这个特定的时间点叫做纪元。纪元采用的UTC是1970年1月1日0时0分0秒。
常用的构造器:
Date()//获取的是当前系统时间
常用方法
long getTime()//返回时间对象距离纪元的毫秒数
SimpleDateFormat
java语言提供了此类型用于格式化日期和解析日期。
构造器
SimpleDateFormat(String format)
说明:此构造器提供了自定义格式来对日期进行格式化或者是解析操作
Y/y:年
M:月
d:日
E:星期
a:上下午
H:24小时制
h:12小时制
m:分
s:秒
方法
String format(Date date)
说明:返回一个指定date对象的按照自定义格式的字符串形式
Date now = new Date();//获取当前系统时间
System.out.println(now);//Wed Jun 26 15:36:45 CST 2019
//将日期对象格式化成某一个字符串表达方式: xxx年xx月xx日 xx时xx分xx秒
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String info = sdf.format(now);
System.out.println("info:"+info);
Date parse(String source) throws ParseException
说明:返回一个指定字符串对象source的Date对象
日历类型:Calendar
1.Calendar是一个抽象类型,不能直接创建对象,但是其提供了一个工厂方法getInstance(),来获取其具体子类型对象。通常情况下,获取的是GregorianCalendar对象,也就是大多数国家采用的阳历。格力高历对象内封装了很多属性,比Date对象要多的多。
2.Calendar也提供了很多常量,用于标记时间分量,或进行计算
static int YEAR
static int MONTH
static int WEEK_OF_YEAR
static int WEEK_OF_MONTH
static int DATE
static int DAY_OF_MONTH
static int DAY_OF_YEAR
static int DAY_OF_WEEK
static int HOUR
static int MINUTE
static int SECOND
3.Calendar提供的常用方法
final Date getTime()
说明:返回当前日历对象的日期形式
void setTime(Date date)
说明:将指定的日期对象,转成日历形式
int get(int field)
说明:获取日历对象内的时间分量
void set(int field, int value)
说明:修改当前的日历对象的属性值,对时间分量field,修改成value
例如:
Calendar c = Calendar.getInstance();
//将日历对象修改成当月的第10天
c.set(Calendar.DATE, 10);
System.out.println(c.getTime());
//再将日历对象修改成2008年8月
c.set(Calendar.YEAR, 2008);
c.set(Calendar.MONTH, 7);
System.out.println(c.getTime());
void add(int field, int amount)
说明:对当前的日历对象的数值值,进行增删。
例如:
//获取50天后的日历信息
Calendar c = Calendar.getInstance(); //2019/6/27
c.add(Calendar.DATE, 50);
System.out.println(c.getTime());
System.out.println((c.get(Calendar.MONTH)+1)+","+c.get(Calendar.DATE));//2019/8/16
集合框架
集合与数组一样,也是一种存储结构(容器),与数组相比,有不同的优缺点。
数组:
a.长度固定
b.可以存储任何Object的子类型对象,也可以存储基本数据类型
c.存储对象时,存储的都是地址信息,即引用
集合:
a.长度可变
b.只能存储引用类型对象
c.存储的是地址信息,即引用
1.Collection接口
说明:Collection是集合框架中的顶级接口。其定义了通用的方法,如
int size();
说明:返回集合中元素的个数,也称之为大小、长度
boolean isEmpty()
说明:返回集合是不是空
boolean contains(Object o);
说明:判断集合中是否存在元素O
Object[] toArray();
说明:将集合转成对应的数组对象,不常用
T[] toArray(T[] a);
说明:将集合转成对应的数组对象,常用
boolean add(E e);
说明:向集合中添加元素
boolean remove(Object o);
说明:移除集合中的元素o
void clear();
说明:清空集合中的元素
2.常用的子接口
a.List接口
定义了一种线性表的存储结构。可以存储重复的元素,同时对元素进行有顺序的标记。默认使用添加进集合的顺序进行索引标记。同时提供了通过索引操作集合元素的方法。
简单的说:list 是一个有顺序的,可重复的一种线性表。
b.Queue(队列)
定义了一种队列形式的存储结构,可以理解为是一种特殊的线性表。队列的特点是:先进先出(FIFO).
c.Set
定义了一种无序的,不可重复的存储结构。
List接口常用的子类
1.ArrayList
是List接口的一个实现类。底层使用了动态数组的原理(见源码242行: elementData = Arrays.copyOf(elementData, newCapacity))。
再进行随即访问get(int index)/set(int index)时,效率非常高(只需要给定索引,直接找到元素)
2.LinkedList
是List借口的一个实现类。底层使用的是双链表(节点:Node)的原理,一个Node上有三个引用变量,分别是开辟的存储空间有存储当前元素的地址item,还有存储上一个节点的地址prev以及下一个节点的地址next
源码:
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
在进行元素的remove/add【插入】需求上,执行效率高(不需要进行数据的移动,只需要断开重新存储上一个节点和下一个节点的地址信息)
3.Vector
也是一种List的实现类型,比较古老,与ArrayList的原理一样,但是为线程安全的,因此效率低下。而ArrayList的方法都是线程不安全的,因此效率非常高。
即使有线程安全需求,现在也不建议使用此类型。
Queue接口的实现类
1.Queue是一种队列的存储结构,原理为先进先出(FIFO).
a.常用方法
boolean offer(E e);
说明:此方法是将指定元素e追加到队列的末端。
E poll();
说明:移除队列的头部元素并返回。
E peek();
说明:查看队列的头部元素,但不删除。
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
System.out.println("元素个数:"+queue.size());
//进队列
queue.offer("zhangsan");
System.out.println("元素个数:"+queue.size());
System.out.println("元素:"+queue);
queue.offer("lisi1");
queue.offer("wangwu");
queue.offer("zhaoliu");
System.out.println("元素个数:"+queue.size());
System.out.println("元素:"+queue);
//让lisi成为对列的头元素
while(queue.peek()!=null&&!queue.peek().equals("lisi")){
String s =queue.poll();
System.out.println("移除的头元素:"+s);
}
System.out.println("头元素:"+queue.peek());
}
==注意:==在对queue进行移除操作时,最好先查看头元素是不是null.即要考虑null这种情况
2.子接口Deque
是接口Queue的子接口,定义了双端队列的存储结构。其意义在于队列的两端都可进可出。
a.常用方法
boolean offerFirst(E e);
说明:从队列的队首进队列。
boolean offerLast(E e);
说明:从队列的队尾进队列。
E pollFirst();
说明:从队列的队首出队列。
E pollLast();
说明:从队列的队尾出队列。
E peekFirst();
说明:查看队列的队首元素
E peekLast();
说明: 查看队列的队尾元素
3.Stack栈
栈也是一种特殊的队列。原理为只能是队列的一端进出,另外一端不能进行如何操作。所以,我们可以使用双端队列Deque,禁止其中一端得操作,只操作另一端来模拟栈结构。
(FILO:first input last output)
a.Deque为栈结构提供的两个方法
void push(E e);
说明:为栈提供的进栈方法,(推,压到栈底)
E pop();
说明:为栈提供的出栈方法。
b.案例演示:
public static void main(String[] args) {
//使用双端队列模拟栈结构,那么,就只能调用push或pop方法
Deque<String> s = new LinkedList<String>();
//进栈
s.push("zhangsan");
s.push("lisi");
s.push("wangwu");
//打印栈元素,默认使用出栈的顺序遍历
System.out.println(s);
//移除栈中的每一个元素
while(s.size()>0){//先判断栈中是否有元素
s.pop();
System.out.println(s);
}
}
Set接口的常用实现类
设计需求:目的是要存储无序的,不可重复的元素。
1.HashSet
这个类型使用hash算法来散列存储元素。是最常用的Set接口的实现类。在向Set集合中存储/查看元素时,我们需要调用equals方法来一一和集合中的元素进行比较。如果集合中的元素比较多时,那么效率是非常低的。原因是因为有可能需要比较到最后一个元素才知道这个元素是否可以存储/存在。那么如何提高效率??为此,采用了hash算法,为每个对象提供hashCode方法,来计算对象的hash值。在内存中,开辟了很多区域,每个区域用于存储一定范围hash值的对象。因此再次存储或者是查看元素时,我们只需要计算出这个对象属于那个区域,然后和此区域中的数据比较,这样的比较效率明显提高了很多倍。简单的说:就是为了提高检查效率。
(一)检查原理(存储或查看):
1.先计算要存储或要查看的元素的hash值(对象的hashCode方法的返回值)。
2.然后再查看集合中是否有此hash值的对象。
a.如果不存在
就可以进行存储、或确定集合中无此元素。
b.如果已经存在
就调用equals方法,进行比较.
如果返回false,说明集合中没有此对象。那么就可以存储此对象,或者确定集合中没有此对象。
==注意:==hash相同,equals不同的这些对象存储在单向链表中
如果返回true, 说明集合中有此对象,不能再继续存储。如果是查看,就找到了此对象。
(二)如何重写hashCode方法
要求:
1.尽可能的让对象的所有属性都参与hash值得运算
2.尽可能的减少hash值得碰撞
3.定义一个素数变量
reg:
public class Student{
private int age;
private String name;
private char gender;
public int hashCode(){
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + gender;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
(三)重写equals方法的注意事项和约定
1.重写了equals方法,有必要重写hashCode方法
2.hashCode值相同,equals方法的返回值不一定为true
3.hashCode值不同,equals方法的返回值一定为false
(四)
1.equals方法的返回值为true,hashCode值也可能不同(但是,hashCode值相同有助于提高效率)
2.equals方法的返回值为false,hashCode值一定不同。
注意:添加到set集合中的元素,不要轻易修改其属性。否则有可能造成内存溢出。
泛型机制
在jdk1.5以前,没有此概念,集合中的元素可以是任意引用类型,在获取元素我们需要进行强制转换,这样无疑增加了代码难度(还要进行判断),不方便。在jdk1.5以后,集合框架中提供了泛型机制,实际上就是"参数化类型",即在创建集合时,用于指定集合中要存储的元素类型,这样提供了编译期间就可以进行类型检查的效果,可以避免类型转换的问题,提高开发效率,减少错误的可能性。
1.如何编写泛型机制
reg:
public class ArrayList<E>{
//此方法使用的泛型机制
public boolean add(E e) {
}
public static void main(String[] args){
ArrayList<String> names = new ArrayList<String>();
names.add("zhangsan")//调用此方法时,编译器会检查是否为泛型规定的类型对象,如果不是,则友情提示
}
}
迭代器Iterator
迭代器主要就是用来遍历集合中的元素的。Collection父接口中提供了获取迭代器的抽象方法(源码: Iterator iterator();)而其子类型实现了此方法,返回一个具体迭代器对象。
1.获取迭代器
ArrayList names = new ArrayList();
//获取迭代器
Iterator it = names.iterator();
2.Iterator类型提供了几个方法
a.
boolean hasNext()
询问是否有下一个元素需要遍历
b.
E next();
返回下一个要遍历的元素
c.
void remove();
移除当前元素
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<String>();
names.add("java");
names.add("c++");
names.add("python");
names.add("hadoop");
/*for(int i=0;i<names.size();i++){
System.out.println(names.get(i));
}*/
//迭代器
Iterator<String> it = names.iterator();
while(it.hasNext()){
String element = it.next();
if("python".equals(element)){
//names.remove(element);//迭代期间,不可以调用集合的remove
it.remove();//这个是可以的
}
System.out.println(element);
}
System.out.println(names.size());
}
Collection的比较接口
1.Comparable
比较接口:
程序员自定义的类型如果想存储在集合中,并且想排序,那么在定义期间此类型就必须要实现此接口。同时重写compareTo(E e)方法,定义比较规则(降序还是升序)
方法:
public int compareTo(Student o) {
// TODO Auto-generated method stub
return 0;
}
比较规则:this对象和形参o比较(升序)
当this - o 返回一个小于0的数,就说明this 小于 o
this - o 返回0 就说明this 与 o 相同
this - o 返回一个大于0的数 就说明this 大于 o
注意:如果使用形参o - this 就是降序
o - this 相当于 -(this - o)
2.Comparator:比较器接口
当Comparable的比较规则不满足我们的需求时,即想重新修改比较规则,但是不能修改源码(reg:Student类型一旦创建,不能轻易修改,否则有可能影响其他模块)。此时,就需要使用比较器接口,重新定义比较规则
Map接口
1.概述
Map接口是集合框架中的另一个父接口。主要维护着有映射关系的数据,这样的数据我们称之为key-value键值对数据。
name address
“张三” 北京市海淀区中关村…
“李四” 长春市朝阳区卫星路长春大学综D601
“王五” 长春市朝阳区卫星路长春大学综D601
“赵六” 长春市朝阳区卫星路长春大学综C601
null 北京市中南海
2.特点
a.key和value,都必须是引用数据类型。
b.做为key的数据,不能重复。value可以重复
c.key可以为null,但是一个Map集合中只能有一个null作为key
3.Map接口的实现类
a. HashMap
(1)存储原理和hash值冲突解决
底层使用了动态数组(跟hash值有关的数组)和链表的数据结构(jdk1.8以后,有加入了红黑树的数据结构来优化HashMap)。存储元素(key-value)时,先计算key的hash值,计算出存储在数组中的位置。然后看这个位置上是否已经存在元素,如果不存在,就存着在此位置上(数组的每个元素都是一个单链表)的链表头元素上。如果已经存在元素,再调用equals方法和链表中的key进行比较,如果相同,就替换value值, 如果不同,就继续存在对应的单链表中的下一个位置上。
b. HashTable
HashMap 和 Hashtable 是 Map 接口的两个典型实现类
区别:
Hashtable 是一个古老的 Map 实现类,不建议使用
Hashtable 是一个线程安全的 Map 实现,但 HashMap 是线程不安全的。
Hashtable 不允许使用 null 作为 key 和 value,而 HashMap 可以
与 HashSet 集合不能保证元素的顺序的顺序一样,Hashtable 、HashMap 也不能保证其中key-value 对的顺序
4.加载因子
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
说明:HashMap对象的初始容量 为 16
static final int MAXIMUM_CAPACITY = 1 << 30;
说明:定义了HashMap存储元素的最大容量
static final float DEFAULT_LOAD_FACTOR = 0.75f;
说明:加载因子(装载因子)为0.75. 这个值是对空间和时间上考虑的一种折中值。
即当存储元素占容量的75%时,进行扩容
static final Entry<?,?>[] EMPTY_TABLE = {}
说明:Entry是HashMap的内部类,用于描述key-value对。通常用于HashMap的遍历
transient int size;
说明:HashMap对象的元素个数
5.常用方法:
public V put(K key, V value)
说明:用于存储key-value对到集合中
public V get(Object key)
说明:用于通过key来获取value对象
public boolean containsKey(Object key)
说明:用于判断集合中是否包含指定key的key-value对
public V remove(Object key)
说明:通过指定key来移除对应的key-value对
public Set keySet()
说明:返回所有的key对象,封装成Set集合对象
public Collection values()
说明:返回所有的value对象,存储在Collection集合中
public Set<Map.Entry<K,V>> entrySet()
说明:将所有的key-value对封装成Entry,并返回存储所有Entry对象的Set集合
6.HashMap的遍历
a.获取key的Set集合,然后再遍历
Set<String> keys = info.keySet();
//foreach循环,底层原理是迭代器原理
for(String key:keys){
//获取key对应的value
String v = info.get(key);
System.out.println(key+":"+v);
}
b.获取entry的Set集合。
Set<Entry<String,String>> entrys= info.entrySet();
for(Entry<String,String> entry:entrys){
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"#"+value);
}
IO流
1.概述:
数据从一个位置上(设备)传输到另一个位置上(设备),我们称之为数据的传输。按照传输的方向来分类,可以分成以下两种情况
a.输入流(InputStream)
数据从外部设置或其他位置传输到当前设备(当前程序)。
b.输出流(OutputStream)
数据从当前设备(当前程序)传输到外部设置(其他位置)。
2.分类情况
a.按照流向分类
输入流
输出流
b.按照处理单位分类
字节流:以字节为单位,进行读或写
字符流:以字符为单位,进行读或写
1000个字节的文件
字节流: 每次读取 10个字节 ---->100次 (也就是说要和设备打交道的次数为100次)
字符流: 每次读取 10个字符(20个字节)–>50次 (也就是说要和设备打交道的次数为50次)
c.按照功能分类
节点流:
就是直接连接两个设备的流。
处理流:
对节点流进行再次包装和处理的流。
3.当使用IO流技术时,
最后一定要关闭流。否则容易造成内存溢出。
字节流
1.概述
字节流的父类分别是InputStream和OutputStream,这两个类型是抽象类型。定义了一些常用的方法。
a.抽象父类InputStream
abstract int read()
说明:每次只读取一个字节,存储在int类型的低八位上。
public int read(byte b[])
说明:将字节读进指定数组b中,最多一次性读取b.length个字节
返回值为实际读取的字节个数
public int read(byte b[], int off, int len)
说明:将字节读进指定的数组b中,存储时偏移量为off,存len个字节。
返回值为实际读取的字节个数
public long skip(long n)
说明:支持指针操作,即,可以跳过n个字节,再读取
public void close()
说明:关闭流方法
b.抽象父类OutputStream
public abstract void write(int b)
说明:每次只写一个字节,写的数据为指定数据b的低八位。
public void write(byte b[])
说明:将指定字节数组b中的元素 写出去。
public void write(byte b[], int off, int len)
说明:将指定字节数组b中的元素 写出去。从偏移量off开始,写len个。
public void flush()
说明:提供此方法,主要是给处理流使用,即将数据冲刷到外部设备中。
public void close()
说明:关闭流操作
2.字节流的常用的实现类
a.常用字节输入流
FileInputStream
(1)构造器
public FileInputStream(String name)
说明:此构造器,是通过一个描述路径的字符串来构造文件输入流对象
public FileInputStream(File file)
说明:通过一个File对象描述路径来构造输入流对象
ByteArrayInputStream
DataInputStream
BufferedInputStream
(0)概述:
缓存字节输入流内部维护了一个8192(8k)大小的缓冲数据。目的是提高
读取效率。每次都先存储到缓冲数组中,当数组满了或者不够下一次存储时,
则一次性冲刷到目标数组中。如果某一次读取的数据大于8k,则直接读取到目标
数组中,不使用缓冲数组。
(1)构造器
public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int size)
b.常用字节输出流
FileOutputStream
(1)构造器
public FileOutputStream(String name)
说明:通过一个描述路径的字符串来构建文件输出流对象
注意:如果路径描述的文件已经存在,那么就会清空
文件内的所有数据,从头开始写。
public FileOutputStream(String name, boolean append)
说明:通过一个描述路径的字符串来构建文件输出流对象
不过,可以进行追加操作。
即,如果路径描述下的文件已经存在,就进行追加操作。
public FileOutputStream(File file)
public FileOutputStream(File file, boolean append)
ByteArrayOutputStream
DateOutputStream
BufferedOutputStream
(0)概述
缓冲字节输出流内部同样维护着一个8k大小的缓冲数组,每次向目标设备中写数据时,先存储到缓冲数组中,当缓冲数组满了或者不够下一次存储时,则会把数据冲刷到目标设备中。如果在写入缓冲数组中,我们指定的长度大于8k,则不会使用缓冲区。直接写入到设备中。
(1)构造器
public BufferedOutputStream(OutputStream out)
public BufferedOutputStream(OutputStream out, int size)
private void flushBuffer()
==注意:==DataInputStream/DataOutputStream这个数据流对象为基本数据类型而设计的流提供了相应的读/写方法
readByte()/writeByte(int v)
readShort()/writeShort(int v)
readInt()/writeInt(int v)
readLong()/writeLong(long v)
readFloat()/writeFloat(float v)
readDouble()/writeDouble(double v)
readBoolean()/writeBoolean(boolean v)
readChar()/writeChar(int v)
如果想要对字节数组进行读写操作,可以使用ByteArrayInputStream/ByteArrayOutputStream
字符流
1.概述
字符流是以字符为单位进行读写操作的。其抽象父类为Reader/Writer,提供了统一的读写方法。
注意:字符流不能读/写非纯文本文件。比如视频,音频,图片这样的文件不能使用字符流任何类型的文件都可以使用字节流进行读取。
2.常用子类
a. InputStreamReader/InputStreamWriter
(1)构造器:
字符输入流:
public InputStreamReader(InputStream in)
说明:通过指定字节流来构建一个字符输入流对象,使用默认的字符集将字节转出字符
public InputStreamReader(InputStream in, String charsetName)
说明:通过指定字节流和字符集来构建一个字符输入流对象
字符输出流:
public OutputStreamWriter(OutputStream out, String charsetName)
说明:通过指定的字节输出流和字符集来构建字符输出流
public OutputStreamWriter(OutputStream out)
说明:通过指定的字节输出流和默认的字符集来构建字符输出流
b.BufferReader/PrintWriter
(1)概述,这个输入输出缓冲字符流内部同样维护着一个缓冲字符数组,默认大小为8k。
针对于BufferedReader来说,其强大功能在于可以按行读取数据。
针对于PrintWrtier来说,其强大功能在于按行写出数据,并且提供了缓冲区自动刷新操作。
(2)构造器
public BufferedReader(Reader in, int sz)
public BufferedReader(Reader in)
public PrintWriter (Writer out)
public PrintWriter(Writer out,boolean autoFlush)
/*
*需求:复制磁盘上的一个较大文件eclipse-java-luna-SR1-win32.zip
*/
BufferedReader br = null;
PrintWriter pw = null;
try {
br = new BufferedReader(
new InputStreamReader(
new FileInputStream("D:/java_day01.txt")));
pw = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream("D:/copy_day01.txt")),true);
String line = "";
while((line = br.readLine())!=null){
pw.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//如果提供了自动刷新功能,那么流如果没有关闭,最后
//一次的缓冲区内的数据也会刷新到磁盘。但是流关闭操作不能忘记。
try {
br.close();
pw.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
序列化
1.概述
序列化:将数据转出二进制的字节序列,这一过程称之为序列化。通常序列化用于两大领域,一个是数据的传输,另一个是数据的持久化。
反序列化:将字节序列转成相应的数据(磁盘中保存的二进制数据转成内存中的对象),这一个过程就称之为反序列化。
2.对象序列化的要求
a.如果对象想要序列化,那么此对象的类型必须要实现序列化接口Serializable
b.实现接口后,系统会默认给类提供一个版本号属性 serialVersionUID,值得注意的是:系统默认提供的版本号,在反序列化时,又一次生成一个版本号,与之前的不一致,因此反序列化会失败。所以,程序员如果想序列化一个对象,那么此类型应该显式提供一个固定的版本号
3.如果想要在序列化过程中,不保存某一个属性的值。那么可以使用transient修饰词来修饰词属性。
public class Student implements Serializable {
private String name = "张三";
private char gender = '男';
private transient int id = 1001;
/**
*
*/
private static final long serialVersionUID = 4206938663223861415L;
public static void main(String[] args) throws Exception{
test2();
}
// 序列化
public static void test1() throws Exception, Exception {
Student student = new Student();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
"D:/aaa.txt"));
//序列化方法
oos.writeObject(student);
oos.close();
}
// 反序列化
public static void test2() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
"D:/aaa.txt"));
//反序列化方法
Student student =(Student) ois.readObject();
System.out.println(student);
}
@Override
public String toString() {
return "Student [name=" + name + ", gender=" + gender + ", id=" + id
+ "]";
}
}
线程:
1.线程和进程的区别
a.进程的概念
1、进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动程调用的指令和本地变量。
2、进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。
b.线程的概念
进程中所包含的一个或多个执行单元称为线程(thread)。一个线程就是进程中的一个顺序执行流。进程拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问。即同一个进程中的多个线程共享一块内存空间和一组系统资源。线程只能归属于一个进程并且它只能访问该进程所拥有的资源。线程本身也有一个供程序执行时的堆栈,在线程切换时,负荷小,因此线程也被称之为轻负荷进程。
c.
(1)进程是操作系统运行的一个任务,线程是进程中运行的一个任务
(2)进程是资源分配的最小单位(相互独立),线程是程序执行的最小单位(cpu 调度的基本单元)。
(3)进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。进程之间的通信需要以 IPC 进行
(4)线程是轻量级的进程,同一个进程中可以包含多个线程。多线程共享进程中的数据,使用相同的地址空间,因此,线程之间的通信更方便,CPU 切换(或创建)一个线程的开销远比进程要小很多。
(5)一个进程结束,其内的所有线程都结束,但不会对另外一个进程造成影响。多线程程序,一个线程结束,有可能会造成其他线程结束
(6)不过如何处理好同步与互斥是编写多线程程序的难点
2.并发原理
多个线程“同时运行”只是我们感官上的一种表现。其实,线程是并发运行的。操作系统将时间划分成很多时间片段,尽可能的均匀分配给每一个线程,获取时间片段的线程被 CPU 运行,而其他线程处于等待状态。所以这种微观上是走走停停,断断续续的,宏观上都在运行的现象叫并发。但不是绝对意义上的“同时发生”。
3.线程的状态
[外链图片转存失败(img-zcIyrIaL-1562890777555)(H:\Blog\img\线程)]
(1)新建状态(New):新创建了一个线程对象。
(2)就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的 start()方法。该状态
的线程位于可运行线程池中,变得可运行,等待获取 CPU 的使用权。
(3)运行状态(Running):就绪状态的线程获取了 CPU,执行程序代码。
(4)阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃 CPU 使用权,暂时停止运行。直
到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
等待阻塞:运行的线程执行 wait()方法,JVM 会把该线程放入等待池中。
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM会把该线程放入锁池中。
其他阻塞:运行的线程执行 sleep()或 join()方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。
(5)死亡状态(Dead):线程执行完了或者因异常退出了 run()方法,该线程结束生命周期。
4.线程的创建
a. 继承 Thread 类,重写 run 方法,创建该类对象,调用 start 方法开启线程。start 方法可以将该线程对象纳入可执行线程池中。切记,不是调用 run 方法
b. 实现 Runnable 接口,重写 run 方法,创建 Thread 类对象,将 Runnable 子类对象传递给Thread 类对象。调用 start 方法开启线程。
同步锁机制
1.概述
当多个线程并发读写同一个临界资源时,会发生“线程并发安全问题”。
常见的临界资源:
a.多线程共享实例变量
b.多线程共享静态公共变量
如果想解决线程安全问题,需要将异步的操作变为同步操作。
a.异步操作:多线程并发的操作,相当于各干各的
b.同步操作:有先后顺序的操作,相当于你干完我再干。
java 提供了一种内置的锁机制来支持原子性,通过关键字 synchronized 来进行同步代码块。
同步代码块包含两部分:
一个是充当锁的对象的引用
一个是由这个锁保护的代码块。
synchronized(同步监视器-锁对象引用){
//代码块
}
每个 Java 对象都可以用作一个实现同步的锁。线程进入同步代码块之前会自动获得锁,并且在退出同步代码块时自动释放锁,无论是通过正常途径退出还是通过抛出异常退出都一样。
获取内置锁的唯一途径就是进入由这个锁保护的同步代码块或方法。
2.合适的锁对象
使用关键字 synchronized 同步方法内的部分代码块时,对于充当锁的对象,应该注意:
多个需要同步的线程在访问该同步块时,看到的应该是同一个锁的对象引用,否则不同步。
通常我们会使用 this 来作为锁对象
3.合适的锁范围
在使用同步块时,应该尽量在允许的情况下减少同步范围,来提高并发的执行效率
4.Synchronized 关键字的作用域
作用域有两种:
第一种,同步方法内的部分代码块,或者全部代码块(相当于给方法直接加锁)通常情况下锁对象都是 this,此时多个线程访问此同步块时会进行同步操作。如果这个对象中有多个同步方法,只要其中一个线程正在访问某一个同步方法,那么其他线程就不能同时访问这个对象中的任何同步方法。注意,不同的对象实例的同步方法互不干扰。
第二种,同步静态方法当我们对一个静态方法加锁时,该方法的锁对象是类对象。每个类都有唯一的一个类对象。获取类对象的方法为:类名.class。静态方法与非静态方法同时声明了 synchronized,他们之间是非互斥关系的。原因就在于,静态方法的锁是类对象,而非静态方法的锁对象是当前方法所属的对象。
Socket套接字
1.概述
java语言中提供了一套标准的Socket套接字用于网络编程。使用TCP/IP协议。并且提供了一套获取输入输出流的方法,用于传输数据。
2.两个类型
a.Socket:用于客户端的编程。
public Socket(String host, int port)
b.ServerSocket:用于服务端的编程
public ServerSocket(int port)
2.并发原理
多个线程“同时运行”只是我们感官上的一种表现。其实,线程是并发运行的。操作系统将时间划分成很多时间片段,尽可能的均匀分配给每一个线程,获取时间片段的线程被 CPU 运行,而其他线程处于等待状态。所以这种微观上是走走停停,断断续续的,宏观上都在运行的现象叫并发。但不是绝对意义上的“同时发生”。
3.线程的状态
(1)新建状态(New):新创建了一个线程对象。
(2)就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的 start()方法。该状态
的线程位于可运行线程池中,变得可运行,等待获取 CPU 的使用权。
(3)运行状态(Running):就绪状态的线程获取了 CPU,执行程序代码。
(4)阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃 CPU 使用权,暂时停止运行。直
到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
等待阻塞:运行的线程执行 wait()方法,JVM 会把该线程放入等待池中。
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM会把该线程放入锁池中。
其他阻塞:运行的线程执行 sleep()或 join()方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。
(5)死亡状态(Dead):线程执行完了或者因异常退出了 run()方法,该线程结束生命周期。
4.线程的创建
a. 继承 Thread 类,重写 run 方法,创建该类对象,调用 start 方法开启线程。start 方法可以将该线程对象纳入可执行线程池中。切记,不是调用 run 方法
b. 实现 Runnable 接口,重写 run 方法,创建 Thread 类对象,将 Runnable 子类对象传递给Thread 类对象。调用 start 方法开启线程。
同步锁机制
1.概述
当多个线程并发读写同一个临界资源时,会发生“线程并发安全问题”。
常见的临界资源:
a.多线程共享实例变量
b.多线程共享静态公共变量
如果想解决线程安全问题,需要将异步的操作变为同步操作。
a.异步操作:多线程并发的操作,相当于各干各的
b.同步操作:有先后顺序的操作,相当于你干完我再干。
java 提供了一种内置的锁机制来支持原子性,通过关键字 synchronized 来进行同步代码块。
同步代码块包含两部分:
一个是充当锁的对象的引用
一个是由这个锁保护的代码块。
synchronized(同步监视器-锁对象引用){
//代码块
}
每个 Java 对象都可以用作一个实现同步的锁。线程进入同步代码块之前会自动获得锁,并且在退出同步代码块时自动释放锁,无论是通过正常途径退出还是通过抛出异常退出都一样。
获取内置锁的唯一途径就是进入由这个锁保护的同步代码块或方法。
2.合适的锁对象
使用关键字 synchronized 同步方法内的部分代码块时,对于充当锁的对象,应该注意:
多个需要同步的线程在访问该同步块时,看到的应该是同一个锁的对象引用,否则不同步。
通常我们会使用 this 来作为锁对象
3.合适的锁范围
在使用同步块时,应该尽量在允许的情况下减少同步范围,来提高并发的执行效率
4.Synchronized 关键字的作用域
作用域有两种:
第一种,同步方法内的部分代码块,或者全部代码块(相当于给方法直接加锁)通常情况下锁对象都是 this,此时多个线程访问此同步块时会进行同步操作。如果这个对象中有多个同步方法,只要其中一个线程正在访问某一个同步方法,那么其他线程就不能同时访问这个对象中的任何同步方法。注意,不同的对象实例的同步方法互不干扰。
第二种,同步静态方法当我们对一个静态方法加锁时,该方法的锁对象是类对象。每个类都有唯一的一个类对象。获取类对象的方法为:类名.class。静态方法与非静态方法同时声明了 synchronized,他们之间是非互斥关系的。原因就在于,静态方法的锁是类对象,而非静态方法的锁对象是当前方法所属的对象。
Socket套接字
1.概述
java语言中提供了一套标准的Socket套接字用于网络编程。使用TCP/IP协议。并且提供了一套获取输入输出流的方法,用于传输数据。
2.两个类型
a.Socket:用于客户端的编程。
public Socket(String host, int port)
b.ServerSocket:用于服务端的编程
public ServerSocket(int port)