2021-09-08

java笔记

Tips

浮点数有误差(所以判断浮点数相不相等就是用浮点数的差值的绝对值与很小的数进行比较)
eg:
	System.out.printfln(Math.abs(a-b)<1e-6);
关系运算的结果只能是 ture 或者false
boolean 值只能是 ture 或者 false
ture和false与c语言不同,不是10,而是状态

优先级

1.单目取正取负自增自减取非  			从右到左
2.乘除取余加减  					从左到右
3.关系运算符(等于不等于优先级最低)			从左到右
4.等于 不等					从左到右
5.&&						从左到右
6.||						从左到右
7.赋值						从右到左

强制类型转换

直接加(类型) 当成单目运算符的优先级

验证

1.测试程序常使用边界数据,如有效范围两端的数据、特殊的倍数等
2.个位数
3.10
4.0
5.负数                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             

标号

在循环前可以放一个标号来标示循环.
eg:   label:
	brack label;
带标号的breakcontinue对那个循环起作用

数组

Scanner in = new Scanner(System.in);


数组初始化方式一:
<类型>[] <名字> = new <类型>[元素个数];   (=表赋值[]表数组类型.表示什么的)
int[] numbers = new int[100];
java new创建的数组默认会给数组初始化为0;

数组初始化方式二:

int[] scores = {87,98,69,54,65,76,87,99};
直接用大括号给出数组所有元素的初始值
不需要给出数组大小,编译器会数

数组内部有个名为length的内部成员,会告诉你它的元素数量.

java数组变量是指针(管理者)
int[] a = new int[10];//初始化数组a
int[] b = a;//把指针a内存内的地址赋给指针b

数组变量之间的比较是判断是否指向(管理)同一个数组

复制数组(比较两个数组)必须遍历源数组将每一个元素逐一拷贝给目的数组

二维数组

二维数组的初始化:

	int[][] score = new int[3][5];
	int[][] a = {
			{1,2,3,4},
		    	{1,2,3},
		      };
	编译器数数
	每行一个{},逗号分开
	最后的逗号可以存在
	如果省了,都补0

二维数组长度 行 a.length
	列a[i].length		      
a[i,j]在java里不存在

For -Each 循环

for(<类型><变量>:<数组>)
{}
for(int k : data)
{
	if( k==X  )
	{
		found =TRUE;
		brea                                                                                                                                                                                                                                                                                                                                                                                                                                                                            k;
	}
}
对于data数组当中的每一个元素,循环每一轮拿出来复制给k.

.运算符

当需要一个类或者对象做事情时候,.运算符

包裹类型

Boolean
Character
Integer
Double
有很多函数可以用

Math类

提供一些函数,比如
Math.abs(-12) //求绝对值
Math.round(10.645)//四舍五入
Math.random()//随机一个[0,1)的数
Math.pow(2,3)//2的3次幂

字符串变量

String s;//首字母大写
String 是一个类,String的变量是指针(管理者)而非所有者//与数组变量一样


new = 创建
String s = new String("a string");
创建了一个String类的对象
用a string 初始化这个对象
创建管理(指针)这个对象的变量s
让s管理(指向)这个对象

还可以用这种方法初始化字符串变量
String s = "hello";
编辑器帮你创建一个String类的对象交给s来管理

字符串连接
用+可以连接两个字符串,+的一边是字符串而另一边不是字符串时,会把另一边表达为字符串然后做连接

输入字符串:
in.next();读入一个单词,单词的标志是空格,空格包括空格、换行、tab

in.nextLine();读入一整行

赋值与数组相同,是让两个字符串变量共同管理一个字符串
比较两个String内容是否相同得用equsls而不是==

对象后接.会出现各种函数

字符串操作

字符串变量是对象的管理者
字符串(数据)是对象,对它的所有操作都是通过"."这个运算符来进行的
它表示对左边的这个字符串做右边的那个操作
这里的字符串可以是变量也可以是常量
equals()//比较是否相等
compareTo()//比较大小关系
length()//获得String的长度
s.charAt(index)
	返回在index上的单个字符
	index的范围是0length() -1
	第一个字符的index是0,和数组一样
但是不能用for-each循环来遍历字符串
s.substring(n)
	得到从n号位置到末尾的全部内容

s.substring(b,e)
	得到从b号位置到e号位置之前的内容
	
s.indexOf(c)
	得到c字符所在位置,-1表示不存在

s.indexOf(c,n)
	从n号位置开始寻找c字符

s.indexOf(t)
	找到字符串t所在的位置

s.lastIndexOf(c)
	从右边开始找

s.startsWith(t)
s.endsWith(t)//看有没有以t开头结尾


这几个函数不是修改字符串本身,而需要新建字符串然后进行复制
s.trim()//删除字符串两端空格
s.replace(c1,c2)//把字符串c1的值替换成c2
s.toLowerCase()
s.toUpperCase()//替换大写小写

字符串本身不可修改,没有任何手段可以直接修改

在switch-case 中使用字符串
switch(s)
{
	case "this":....
	case "that":....
}

类型不匹配

当函数期望的参数类型比调用函数时给的值的类型宽的时候,编译器能悄悄替你把类型转换好

char->int->double

当函数期望的参数类型比调用函数时给的值的类型窄的时候,需要自己强制转换

当函数期望的参数类型比调用函数时给的值的类型之间无法匹配不能转换

函数

函数每次运行,产生了一个独立的变量空间,定义在这个函数内部的变量就是本地变量,参数也是本地变量

生存期:变量开始与消亡
作用域:在什么范围内可以访问这个变量(这个变量可以起作用)
对于本地变量,都是在大括号内-

对象和类

对象是实体,需要被创建,可以为我们做事情
类是规范,根据类的定义来创建对象

对象:表达东西或事件,运行时响应消息

类:定义所有猫的属性, 是java中的类型,可以用来定义变量

对象=属性+服务

数据:属性或者状态
操作:函数

把数据和数据的操作放在一起就是封装


oop特性

一切都是对象
程序就是一堆互相发送消息对象
每个对象都有自己的存储空间,里面是其他的对象
每个对象都有一个类型
所有属于某个特定类型的对象可以提供相同的服务

创建对象

new VendingMachine();
VendingMachine v = new VendingMachine();
对象变量是对象的管理者

让对象干活
.运算符

函数与成员变量

在函数中可以直接写成员变量的名字来访问成员变量

this是成员函数的一个特殊的固有的本地变量,它表达了调用这个函数的那个对象,所以会出现this.

成员变量

定义在类的内部,函数的外部的变量是成员变量,成员变量的生存期是对象的生存期,作用域是类内部的成员函数

成员变量定义初始化

成员变量在定义的地方就可以给出初始值
没有给出初始值的成员变量会自动获得0值
	对象变量的0值表示没有管理任何对象,也可以			主动给null值
	定义初始化可以调用函数,甚至可以使用已经定义的成员变量 

构造函数

如果有一个成员函数的名字和类的名字完全相同,则在创建这个类的每一个对象的时候会自动调用这个函数 --- >构造函数

函数重载

一个类可以有多个构造函数,只要他们的参数表不同

创建对象的时候给出不同的参数值,就会自动调用出不同的构造函数

通过this()还可以调用其他的构造函数

一个类里的同名但参数表不同的函数构成了重载关系

构造函数的参数在初始化的时候给

private

私有权限:
		只有这个类内部可以访问
		类内部指类的成员函数和定义初始化
		这个限制是对类的而不是对对象的 (意味着对类来说,每个在类内部可以访问相同类的不同对象的数据)

public

任何人都可以访问:
	任何人指的是在任何类的函数或定义初始化中可以使用
	使用指的是调用、访问或定义变量

如果class要写成public,则文件名必须与class相同,一个文件就是一个编译单元且一个编译单元只能有一个public

protect

protect型是只能在同一个包内和子类能够调用和查看

friendly

如果前面没有限定词,就认为是friendly型,只有在同一个包内可以访问

包是管理类的一种手段,用另一个包时,我们需要import.;
或者import..;要全部加进去时可用 import.*;

static

标明static的是类所有,也就是被类所有,所有对象都共用一份类变量.

static的函数只能调用static的函数,只能去访问static的成员变量,static的函数和static的成员变量都可以通过类的名称去访问,也可以通过某个对象去访问,通过对象去访问时不能得到对象的具体信息

容器类

ArrayList 是系统内部的一个类,能够记录保存元素,也能算出保存元素个数

ArrayList可以无限存东西,并按照存入顺序存入一定的下标,也有增删插存入某数组返回元素个数等功能

ArrayList<String> notes = new ArrayList<String>;

容器类有两个类型:
	容器的类型
	元素的类型

对象数组

对象数组中的每个元素都是对象的管理者(指针)而非对象本身 
eg: String[] a = new String[10];初始化后每个String[n]内都是null//指针初始化为空

对象数组的for-each

由于对象数组的变量是对象的管理者,多疑for-each运行时的k复制了对象变量的指针(也就是指向对象),所以操作是一样的

ArrayList 不是数组 也可以用for-each

集合容器

初始化集合容器:
	HashSet<String> = new HashSet<String>();

集合容器没有排序
集合容器和数学一样不会重复;

任何一个类里面如果有 public String toString(){ return ""+i; }
就能用System.Out.Println()直接输出

Hash表

初始化Hash:Hash表是一个key对应一个Value
HashMap<key,value>//key和value都必须是对象

private HashMap<Integer, String> coinnames = new HashMap<Integer, String>;
因为都是对象,所以只能用Integer ,但也可直接用int形的数字

Hash表的for-each

HashMap<String,Room> exits = new HashMap<String,Room>();
for(String dir : exits.keySet())
{

}

继承

子类继承所有父类的public和protect型的成员变量和函数
子类初始化时要先去父类那初始化继承过来的成员变量(先定义初始化,在构造初始化)继而初始化自己的成员变量()定义初始化,在构造初始化)
super();子类把得到的数据传给父类初始化
调用父类同名函数时候,可以使用super.同名函数();
如果子类当中有父类的同名的成员变量,那么就会有两个变量,在子类中使用的当然是自己的,父类里使用的也是父类的.
子类表示为某父类的继承时 表示为:public class 子类 extends 父类{}

子类和子类型

类定义了类型
子类定义了子类型
	子类的对象可以当作父类的对象来使用
		可以赋值给父类的对象
		传递给需要父类对象的函数
		放进存放父类对象的容器里

多态变量

java的对象变量是多态的,他们能保存不止一种类型的变量
他们可以保存的是声明类型的对象,或声明类型的子类的对象
当把子类的对象赋给父类的变量的时候,就发生了向上造型

造型cast

子类的对象可以赋值给父类的变量(也就是父类的变量(指向)管理子类的对象)//注意!java中不存在对象对对象的赋值

父类的对象不能赋值给子类的变量
	Vechicle v;(父类变量)
	Car c = new  Car();//子类对象
	v = c;//可以
	c = v;//编译错误
可以用造型:
	c = (Car) v;//只有当父类v指向(管理的)是Car才能这么做

造型

用括号围起来类型放在值的前面

对象本身并没有发生任何变化
	所以不是类型转换

运行时有机制来检查这种转化是否合理
	ClassCastException

向上造型

拿一个子类的对象,当作父类的对象来用

向上造型是默认的,不需要运算符

向上造型总是安全的

函数调用的绑定

当通过对象变量调用函数的时候,调用哪个函数这个事情叫做绑定

	静态绑定:根据变量的声明类型来决定
	动态绑定:根据变量的动态类型来决定(java默认)

在成员函数中调用其他成员函数也是通过this这个对象变量来调用的

覆盖override

子类和父类中存在名称和参数表完全相同的函数,这一对函数构成覆盖关系

通过父类的变量调用存在覆盖关系的函数时,会调用变量当时所管理的对象所属的类的函数

Object

java是单根结构,所有类都是Object的子类,不用声明,默认是object的子类

toString()//返回一个对象的字符串的表达形式,用字符串表达那个对象
equals()就是从Object中继承过来的//equals()就是比较两个对象是否相等而不是表示两个对象是否管理同一个对象.

@Override 是检查函数是否完全覆盖了父类函数的一种手段

而如果没有覆盖父类函数,有可能就不会被调用

代码规范


1.消除代码复制
2.要有可扩展性
3.用封装来降低耦合//类与类之间的关系称作耦合,耦合越低越好,保持距离是形成良好代码的关键
4.尽量让所有的数据也就是成员变量私有

StringBuffer

StringBuffer 是一个可以修改的变量,不像String一样,每次加入一个字符串都需要生成一个新的string对象然后指向它,系统开销比较小

StringBuffer sb = new StringBuffer();
sb.append(" ");//可加入一些新的东西

可扩展性

用接口来实现聚合
	给room类实现的新方法,把方向的细节彻底 隐藏在Room类内部了
	今后方向如何实现就和外部无关了

用容器来实现灵活性
	Room的方向是用成员变量表示的,增加或者减少方向就要改变代码
	如果使用Hash表来表示方向,那么方向就不是硬编码了

以框架+数据来提高可扩展性
	命令的解析是否可以脱离if-else
	定义一个Handler来处理命令
	用Hash表来保存命令和Handler之间的关系

抽象函数/抽象类

抽象函数  ------ 表达概念而无法实现具体代码的函数
抽象类 	------ 表达概念而无法构造出实体的类

带有abstract修饰符的函数
有抽象函数的类一定是抽象类//也用abstract修饰
抽象类不能制造对象,但是可以定义变量.任何继承看抽象类的非抽象类的对象可以赋给这个变量

实现抽象函数

继承自抽象类的子类必须覆盖父类中的抽象函数否则自己就会变成抽象类,就不能制造对象出来

两种抽象

与具体相对
	表示一种概念而非实体
与细节相对
	表示在一定程度上忽略细节而着眼大局

接口

接口定义用interface
接口是纯象类
	所有的成员函数都是抽象函数
	所有的成员变量都是public static final//类的变量,且是常数
接口规定了长什么样,但是不管 里面有什么


实现接口格式
eg:
public class Fox extends Animal implements ell
{
}

类用extends表继承,接口用implements表实现
类可以实现很多接口
接口可以继承接口,但不能继承类
接口不能实现接口

面向接口的编程方式

设计程序时先定义接口,再实现类
任何需要再函数之间传入传出的一定是接口而不是具体的类

布局管理器


swing管理的布局管理器
frame.add(the.View,BorderLayout.CENTER);
BorderLayout.CENTER
BorderLayout.WEST
BorderLayout.SOUTH
BorderLayout.EAST
BorderLayout.NORTH

控制反转(swing的消息机制)

通过接口给按钮按下去注入信息并运动

内部类

java内部类是一个成员,可以访问其他成员函数,其他成员变量;

定义在别的类内部函数内部的类
	内部类能直接访问外部的全部资源
		包括任何私有的成员
		外部是函数时,只能访问那个函数里的final的变量

匿名类

new对象的时候给出的类的定义形成了匿名类
匿名类可以继承某类,也可以实现某接口
Swing的消息机制广泛使用匿名类//函数内部类

### JTable

JTable类可以以表格的形式显示和编辑数据,JTable类的对象并不存储数据,它只是数据的表现

MVC

数据表现控制三者分离,各负其责

M=Model(模型)
V=View(表现)
C=Contol(控制)

模型:保存和维护数据,提供接口让外部修改数据,通知表现需要刷新
表现:从模型获得数据,根据数据画出表现
控制:从用户得到输入,根据输入调整数据

异常

异常: exception
把有可能出现异常的地方放如try,然后用catch捕捉,如果被catch捕捉到了,就会跳过异常部分,catch里处理
eg:
	try{
		//可能产生异常的代码
		
		} catch(Type1 id1{
		// 处理Type1异常的代码
		} catch(Type2 id2{
		  // 处理Type2异常的代码
		} catch(Type3 id3{
 		 // 处理Type3异常的代码
 	    	}finally{
		//一定要进finally
	}
  
异常一旦发生并不意味着程序一定要中止,而是需要找到一个合适的地方去处理异常 


进入try的四种下场

无疾而终
发生了异常,而且捉到了
发生了异常但是没有捉到
发生了异常捉到了,但是又抛出了

在离开try之前,最终都要进finally

捕捉异常后可以做什么

拿到异常对象之后
	String getMessage();输出异常消息号
	String toString();//输出异常的名称类型还有消息号
	void printStackTrace();//调用堆栈,可以打印异常过程中所经历的函数
但是肯定回不去了,而具体的处理逻辑则取决于你的业务逻辑的需要

再度抛出

catch (Exception e){
	System.err.println("An exception was thrown")
	throw e;
	
}

如果在这个层面上需要处理,但是不能做最终的决定,可以用throw再次抛出,让下一个catch去捕捉

什么能抛

任何继承了Throwable类的对象
Exception类继承了Throwable
	throw new Exception();
	throw new Exception("HELP");

所有的异常都继承自Exception,Exception继承自Throwable,所以当我用

catch(Exception e){
	system.err.println("Caught an exception")
}时候,能捕捉任何异常.

异常捕捉时,子类异常可以用catch(父类)去捕捉




用上异常机制

try{
	open the file:
	determine its size;
	allocate that much memory;
	read the file into memory;
	close the file;//正常业务逻辑
}catch ( ){
}catch ( ){
}catch ( ){
}

 把正常业务逻辑和异常处理机制分割开了;

异常声明

如果你的函数可能抛出异常,就必须在函数头部加以声明
	void f () throws TooBig, TooSmall, DivZero {//...
	void f () {..

你可以声明并不会真的抛出的异常
	你必须把函数的调用放在try块中,并设置catch老捕捉所有可能抛出的异常;或者声明自己会抛出无法处理的异常


异常声明遇到继承关系

当覆盖一个函数的时候子类不能声明抛出比父类的版本更多的异常
在子类的构造函数中,必须声明父类可能抛出的全部异常,也可以多抛

运行时刻异常

ArrayIndexOutOfBoundsException这样的异常是不需要声明的
但是如果没有适当的机制来捕捉,就会最终导致程序终止

标准输入输出流

system.in 
system.out
对字节进行处理

文件流

FileInputStream
FileOutputStream

对文件作读写操作
实际工程已经较少使用

更常用的是以在内存数据或者通信数据上建立的流,如数据库的二进制数据读写或网络端口通信
具体的文件读写往往有更专业的类,比如配置文件和日志文件

流过滤器

DataInputStream DataOutStream 用以读写二进制的方式表达的基本数据类型的数据 

Reader/Writer 

二进制数据采用InputStream/OutStream 

文本输入输出用Reader/Writer 

过滤器思路输出(把二进制数据输出到文本文件中) 
DataOutputStream out =new DataOutPutStream( 
		new BufferedOutputStream( 
			new FileOutputStream("a.dat") ))): 

过滤器思路输出(把文本文件中的二进制数据输入到代码里) 
DataInputStream in=new DataInputStream(  
		new BufferedInputStream(   
			new FileOutputStream("a.dat") )): 


过滤器思路(在流上建立文本处理,输出范例)
PrintWriter out = new PrintWriter(
		new BufferedWriter(
			new OutputStreamWriter(
			new FileOutputStream("a.txt"))));
			
过滤器思路(在流上建立文本处理,输入范例)
	BufferedReader in = new BufferedReader(
		new InputStreamReader(
			new FileInputStream("src/hello/Main.java")));


配合readLine()
LineNumberReader可以得到行号 getLineNumber()

FileReader


InputStreamReader类的子类,所以的方法都从父类中继承而来


FileReader(File file)//在给定从中读取数据的File的情况下创建一个新的FileReader


FileReader(String fileName)//在给定从中读取数据的文件名的情况下创建一个新的FileReader

FileReader不能指定编码转换方式

PrintWriter

format("格式")
printf("格式")
print(各种基本类型)
println(各种基本类型)//输出带换行符

Scanner

在inputStream或Reader上建立一个Scanner对象可以从流中的文本中解析出以文本表达的各种基本类型

next()

如果是二进制数据:Stream(InputStream/OutStream)

如果是文本文件并想读入文本(Reader/Writer)

如果是文本文件并想读入其中的数据(格式化文本)(Scanner)

阻塞/非阻塞

read()函数是阻塞,在读到所需内容之前会停下来等

使用read()的更"高级"的函数,nextInt(),readLine()因为底层调用的还是read(),所以都是这样

所以常用单独的线程来做socker读的等待,或使用nio的channer选择机制

对于socket,可以设置SO时间,setSoTimeout(int timeOut)

对象串行化

首先类得实现接口
class Student implements Serializable{}(实现的接口叫可以串行化的类)


直接写进去的方式:
		ObjectOutputStream out = new ObjectOutputStream(
			new FileOutputStream("文件名.dat"));

		out.writeObject(student对象);
		out.close;

直接读出来的方式:
	ObjectInputStream in =  new ObjectInputStream(
		new FileInputStream("文件名.dat"));

	Student s2 = (Student)in.readObject();//读入student类