Java学习笔记
java 常用指令.
Scanner in = new Scanner(System.in); //初始化输入模块
System.out.println(); //输出指令
in.nextlnt(); //读下一个整数
in.next(); // read the input only till the space. It can’t read two words separated by space.
in.nextLine(); //reads input including space between the words (that is, it reads till the end of line \n).
java的 数字类型
基本同C.
但是float类型的数字后面要跟f,double类型的数字要跟d(或者省略).
比如: float price,num;
num = price * 1.2;
那么绝对会报错. cannot convert double to float.
因为1.2 默认是double.
应该这么写: num = price * 1.2f
则ok.
10 是整型.
10.0 是double型.
10.0f 是float 型.
在Java里推荐使用double型,而不是float.
使用float需要强制类型转换. 默认小数输入进去即可与double进行计算. 整数与浮点数运算后会自动变为浮点数.
break
一般break 都是只会跳出当前循环,但是如果在循环前打上标号(相当于给循环起了名字):
在循环前可以放一个标号来标识循环. break 后+ 标号 ,则会跳出标号的循环.
如;
QUEEN: //标识加冒号
for{
for{
for{
` break QUEEN;}}}
则会直接跳出最大那个循环.
数组
- <类型>[]<名字> = new<类型>[元素个数]
如:
int[] grades = new int[100];
double[] averages = new double[20]; - 注意:
元素个数必须是整数.
元素个数必须给出.
元素个数可以是变量.
java的数组使用new创建后自动就是各项为0;
也可以
int[] qwasd ={85,5,2} 这样来初始化
3… 数组变量是数组的管理者,而并非数组本身.
数组必须创建出来后交给数组变量来管理.
数组变量之间的赋值是 管理权限的赋予.
数组变量之间的比较是判断是否管理同一个数组.
如
int[] a={1,2,3};
int[]b =a ;
b[0]++;
则a[0]会变为2.不再是1.
因为a和b本质上指向的是同一块地址.
如果需要重新创建一片区域,则必须使用遍历的方法,对原数组的内容进行拷贝.
for each循环
for(<类型> <变量> : <数组>)
{
}
此循环中,k会自动的从数组的第一个元素遍历到最后一个元素.类似于python中的for循环.
二维数组:
int[][]a = new int[5][6];
也可以初始化:
int [][]a = {
{1,2,3,4},
{1,5,8},
};
每行一个{},逗号分隔.
逗号可以存在. 省略表示补零.
字符串
创建字符串
String s = new String("<初始化的内容>");
也可以:
String s = “<初始化的内容>”;
字符串的比较:
== 比较的是两个字符串是否管理着同一块区域。
而equals( )函数则会比较两个字符串的内容是否相同.
如:s.equals§ 是比较 s和p两个字符串的内容是否相同.
而 s==p 是判断s和p是否管理着同一片区域.
字符串的其他函数.
String s = new String();
s.length() // 求字符串s的长度.
s.charAt() // 访问字符串中某个特定位置的字母 index的范围是从0到length-1 但是不能用for-each来遍历.
s.substring(n) //获取子串. 从第n位开始到末尾.
s.substring(b,e) //获取子串,从第b位到第e位 首包含 尾不包含
s.indexOf© // 获取c字符在s中的位置,若返回-1 则表示不存在
s.indexOf(c,n) //获取c字符在s中的位置,从n开始寻找. 若返回-1 则不存在.
s.indexOf(t) //获取字符串t在s 中的位置.
s.lastindexOf©
s.lastindexOf(c,n)
s.lastindexOf(t) //从尾部向首部 寻找位置(倒着来)
s.startsWith(t)
s.endsWith(t) //查询字符串s是否是以字符串t开头或结尾的
s.trim() //删除字符串两端的空格.
s.replace(c1,c2) //将字符串s中的c1全部替换为c2
s.toLowerCase //将字符串中的所有字母都变成小写
s.toUpperCase //将字符串中所有的字母都变成大写
所有字符串都是不可变的,所以以上所有操作都不可改变原来的字符串,而是新产生字符串.因为他们只是管理者.
switch case 中的case也可以使用字符串
case"this"
case"that"
Math类函数
Math.abs(n) 求n的绝对值.
Math.round(n) 四舍五入求n的整数值.
Math.random() 会产生一个0到1之间的随机数.
Math.pow(m,n) 求m的n次幂.m n可以为浮点数.
值传递
java函数中永远进行的只是值传递,而不是变量的传递.
a=5;
b=6;
public static void swap(a,b)
{
int t;
t=a;
a=b;
b=t;
}
这个函数并不能实际交换ab的值,因为java函数只进行的值交换.
本地变量不会被默认初始化
对象与类
对象是实体,需要被创建,可以为我们做事情.
而类是规范,根据类的定义创建对象.
对象=属性+服务.
蛋图(数据是蛋黄,对数据的操作是蛋清.)
把数据和对数据的操作放在一起,称为封装.
this 指代的是当前操作的对象.在使用成员函数的时候,<对象名>.<成员函数>会用this这样一个内置的对象返回对象的id.
在一个成员函数中调用另一个成员函数不需要加this.他会自动以当前对象来执行.
成员变量的作用域是在类的各个函数域内.
未初始化的成员变量在new时会自动被赋予初始值0.
而未初始化的本地变量不行,会报错.
成员变量在初始化的时候可以直接赋值,也可以调用成员函数.
构造函数
- 如果有一个成员函数的名字和类的名字完全相同,则在创建这个类的每一个对象的时候都会自动调用这个函数. 这个函数称为构造函数.
2.构造函数坚决不能有返回值. void类型也不可以.构造函数直接写<类名>+大括号就可以了.
函数重载(OverLoad)
一个类可以有多个构造函数.同个类中,函数名相同但是参数表不同的情况叫做函数重载.
如 VendingMachine(){}
VendingMachine(int price)
这两个构造函数就是重载的关系.编译器通过给的参数的不同选择使用不同的函数.
在一个构造函数中,还可以在首行通过this()来调用其他的构造函数,不过必须在第一行,也只能调用一次.
private
成员变量无特殊理由,一般都要声明为private类型.
private类型只有类的内部可以访问.
类的内部指类的成员函数与类的初始化.
private的边界是针对类的,而非针对对象的.所以两个同一类的不同的对象可以相互访问她的private.
public
public是指开放的访问属性.
任何人都可以调用public声明的函数.
如果一个函数或一个类前没有声明"public"或者"private",那么他就是"friendly"属性.
friendly属性的函数和类只能被在同一个包中的其他文件中调用.
如果对类进行public,那么这个类, 必须放在和他自己类名相同的编译单元里(即一个.java)文件.否则编译不会通过.
一个编译单元里只能有一个public 的类.
类变量
public static int step = 2;
如果一个类里面有被static修饰的变量(step),那么这个变量称为类变量.
类变量不同于其他的成员变量,成员变量是隶属于单独的对象,而类变量属于自己的类.
也就是说 只要对象属于这个类,那么这个step的值一定一样的. step这个类变量是类自带的,而不属于任何一个对象.
类函数
被static 修饰的函数称为类函数,类函数不属于某一个对象,它属于这个类.
类函数只能调用类变量.(静态方法只能访问静态变量).
因为你如果要访问其他的成员变量的时候,他找不到.
容器类
ArrayList notes = new ArrayList();
容器类有两个类型. ArrayList是类名,代表容器类.String是元素类型,代表这个容器内装的是String. 而notes是这个ArrayList 的对象的名字.
ArrayList 的成员函数:
<对象名>.add(); 增加一个对象进入容器中.
<对象名>.size(); 现在容器中有多少个对象.
<对象名>.get(); 获得对应下标处的文本.
<对象名>.add(int index; element) 往指定位置中加入元素.
<对象名>.remove(index) 删除指定位置的元素,返回被删除的元素.
<对象名>.toArray)(String[] a) 将容器中的字符串装入a中.
对象数组
对象数组中,每个元素都是对象的管理者,而非对象本身.
所以在没有初始化的时候,相当于对象数组中的管理者还没有管理任何东西.
这是不同于普通类型的数组的.
对象数组的FOR-EACH循环不同于普通数组的FOR-EACH循环
如对 int[] 数组. for-each循环中如果给每个遍历的k进行k++;是不会起到任何效果的.
但是对于一个string[]数组,对于每次遍历中的k执行操作,是会对相应index位置上的string起作用的.这是因为在令k = String[]时,相当于k也变成了管理者.
HashSet容器
HashSet也是一种对象容器.
它与ArrayList不同,HashSet是一个数学概念的上集合.
所以里面的元素不会重复(即使你输入了两次同一个内容)
元素也没有顺序,只会随机的读出所包含的元素.
String toString() 函数
我们在实测中发现,对于对象容器 ArrayList 还有HashSet ,可以直接
System.out.println(<某对象容器的名称>);
来输出对象容器中所包含的元素.
究其原因,是因为ArrayList和HashSet中都包含了一个成员函数String toString().
只要一个类中含有这样一个成员函数,必须名字就是’‘toString’’. 那么就可以直接println这个类的对象.
HashMap类型
HashMap类型称为hash表. 一般用于两种不同类型的变量进行对应.
初始化方法:
HashMap<<包裹类型>,<包裹类型>> <对象名> = new HashMap<<包裹类型>,包裹类型>();
注意:
<>内的两个数据类型分别代表了两个表中要装的数据类型.
这两个数据类型必须是包裹类型,而不是一个基本数据类型.(如int不能写为int,要写Integer).
HashMap中有许多内置函数.
常用的有:
<对象名>.put(key,value); 向表中添加元素.
<对象名>.get(key) 查询对应key的value值.
<对象名>.containsKey(key) 查询是否有key值对应的value.
<对象名>.keySet() 获得所有key的HashSet的容器集合.
还需要注意的是:
Hash表中同一个key值只能对应一个Value,所以:
先输入的key对应的value 会被后输入的同一个key的value所覆盖.
继承关系: 父类与子类
如果现在有两个类:DVD和CD 他们成员变量都有title,都有time等等相同的地方,那么代码的重复度会很高.这个时候需要引入父类.
我们假设有一个父类叫做item:
父类和子类之间的关系称为继承.继承指的是子类会继承父类的成员变量以及成员函数,在需要时自动调用之.
public class CD extends item. 表示CD继承item.
所以我们就可以创建一个父类item,将cd和dvd类中的重复的成员变量与成员函数集成到父类来.
若调用了某个成员变量或者成员函数,对于子类,一般步骤是先去查询父类的成员函数与成员变量,再查询子类的成员变量和成员函数.
class item{
private title}
那么这样可以吗?
答案是不行的.
父类的变量声明:protected
父类的变量声明如果用的是private.那么子类不能调用这个成员变量,会提示invisible.
所以我们应该用protected声明,用protected声明的成员变量可以被包内的其他类和子类调用.
super()函数与子类的构造.
我们知道子类的很多成员变量是声明在父类中的,那么在构造时,应该怎么做呢?
答案是 super()函数.
super(<数据类型>)函数的意思是: 去寻找父类的符合括号内数据类型的构造函数.
public class item{
public time;
public title = ok;
item(int t)
{
item.time = t;
}
而子类的构造函数应该写作:
cd(int t)
{
super(t);
}
在构造一个子类时: 执行顺序是:先初始化父类的成员变量,再执行super函数.再初始化子类的成员变量,最后执行子类的构造.
即使子类的构造函数不需要调用父类的构造函数,在程序执行时,不管你写没写super(),程序都会自动去执行super()即参数为空的一个父类的构造函数. 所以父类一定要有一个参数为空的构造函数,否则会报错.
父类成员变量与子类成员变量
子类若含有和父类同名的成员变量的话,在实际的存储区其实是会有两个同名成员不同id的成员变量的.
在子类中操作时,默认赋值的是子类的成员变量. 而对父类操作时,是对父类的成员变量操作.另一个都是隐藏状态.
多态
一个对象变量有两种态: 声明类型 和 动态类型.
动态绑定和静态绑定
在调用对象函数的时候,使用当前动态类型的成员函数称为动态绑定.
使用声明类型的成员函数称为静态绑定.
在JAVA中,都是动态绑定.
也就是说 一个父类的对象在使用一个具有覆盖关系的函数时,要看它的动态类型处于哪一个子类的状态.然后调用该子类的函数.
覆盖(overload)
父类和子类中存在着 函数名 和 参数表 均相同的函数.
这种函数称为覆盖函数.overload
具有覆盖类型的函数在调用时,遵循动态绑定原则.
造型
造型分为向上造型和向下造型.
向上造型是指 子类的对象被当做父类对象,调用给需要父类对象的函数或接口使用.
向下造型是指 父类的对象被当做子类对象,调用给需要子类对象的函数或接口使用.
通过对继承的学习,我们知道:
- 向上造型是始终安全的.
- 向下造型需要父类当前的动态类型确实是子类的时候,才可以向下造型.
- 造型并没有改变任何东西,它只是将一个类型的对象"看做"另一个类型的对象在使用.
- 造型的方法: 括号加造型的类名+ 变量名. 如(CD) item.
Object 类.
Java是一个单根系统. 也就是说所有的类其实都是object的子类.Object是一个Root.
toString() 函数
toString函数是一个可以将任何对象转换为String输出的函数.
在使用String时,必须对子类声明一个覆盖的toString()函数.
可以使用Source功能来构造overload的toString函数.
之后便可以直接把该类的对象当做string来输出.
equals()函数
equals()是一个判断两个同类型的对象是否相等的函数
可以通过构造具有覆盖关系的成员函数来实现.(source功能也可以帮助构造.)
耦合
- 在写代码的过程中,经常会遇到两个类有很多出口与代码相关.
- 这种类与类之间的关系称之为耦合.
- 耦合是一种不好的现象,他会降低代码的可扩展性. 理想的代码状态是低耦合状态. 所以我们说耦合越低越好,保持距离是形成良好代码的关键.
抽象
接口
接口是纯抽象类。
所有成员函数都是抽象函数。
所有变量都是public static final。
类用extends继承,接口用implements 实现。
在Java中不允许多继承,但是允许实现多个接口。
接口可以继承接口,但不能继承类,更不能实现接口。
面向接口的编程模式
设计程序时先定义接口。
任何需要在函数内传入传出的一定是接口,而不是某个特殊的类。
控制反转
JButton的ActionListener,回调函数都属于控制反转。
当对某类中某一事件关心时,可以预留出一个接口,在类中构造一个成员变量Listener,并编写一个注册函数。在需要对事件进行处理时,通过实现这个接口来覆写接口中预留出的函数,通过函数注册进去。调用时,系统会反过来调用你写的函数。
这便是控制反转,具体可以参考系统分析第一次作业。
MVC设计模式
MVC设计模式中:
M 指的是 model 数据模型。
V 指的是 view 可视界面。
C 指的是 control 控制。
MVC设计模式,指的是将 数据,界面,控制三者分离进行设计的一种思路。三个模块都十分单纯,只管自己的。尤其是V与C之间毫无干涉。 三个模块之间的交互通过接口来实现。
异常机制
异常是Java中的一种报错的方式。在程序运行中,可能出现异常,譬如内存泄漏等情况,这种时候就需要对异常进行处理。
对异常进行处理的方法是:try catch 具体操作如下:
try{
//可能产生异常的代码
}catch(Type id1)
{//处理type1类型的异常
}catch(Type id2)
{//处理type2 类型的异常
}
拿到了异常后可以进行的操作:
- String getMessage();
- String toString();
- void printStackTrace(); // 追溯异常发生的地方以及它的调用过程。
- throw e //再度抛出,捕捉到后觉得处理不了,移交给上级进行处理。
抛出异常的函数
如果你调用一个声明抛出异常的函数,那么你必须:
- 把函数放在try块里,并用catch捕捉其所有的异常情况.
- 或者 声明自己会抛出无法处理的异常.
异常声明遇到继承关系
- 当覆盖一个函数时,子类不能声明抛出比父类更多的异常.
- 在子类的构造函数中,必须声明父类可能会出现的全部异常.
流
- 流是输入输出的方式.
- 流是一维单向的.
字节流
InputStream
OutputStream
文件流
FileInputStream
FileOutputStream
对文件做读写操作。 在实际操作中已经较少使用了。 只能写字节。
流过滤器
以一个介质流对象为基础层层构建过滤器流,最终形成的流对象能在数据的输入与输出过程中,逐层使用过滤器流的方法来读写数据。
如:
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(“a.dat”)));
这样就实现了不止一个字节的读写。二进制文件
文本流
二进制数据使用InputStream 和 OutputStream。
文本数据采用Reader 和 Writer。
在流的基础上通过过滤器来建立流的输入和输出:
如:PrintWriter pw = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter (
new FileOutputStream("abc.txt’ ))));
读入常常使用的是BufferedReader,通过readLine()来一行行读入文本。
实例如下:
后面的那个While语句,是为了让他读到末尾时停下来。
汉字问题
在Java中,默认国标码读汉字是不存在问题的。但是当汉字使用UTF8等编码时,可能会存在乱码的问题。这是由于File流的问题所导致的。
我们可以通过以下方式处理。
流的选择
阻塞/非阻塞
对象串行化
我们如果想要把一个对象中的所有成员变量保存在一个文件中,以供存读。那么应该怎么办呢?
答案是对象串行化。
首先在构建类的时候,该类必须实现一个叫做Serializable的接口,也应该实现toString的方法。
然后我们便可以用
可以使用这些方法来处理对象。
实例如下:
使用ObjectOutputStream 向外输出。
使用ObjectInputStream 读取内容。