Java程序运行
- Java程序的执行必须经过编辑、编译和运行三个步骤
- 编辑指编写代码,最终形成后缀名为
.java
的Java源文件 - 编译指使用Java编译器(
javac
指令)将源文件翻译为二进制代码,编译后生成后缀名为.class
的字节码文件,该字节码文件并不是一个可以直接运行的文件。Java编译器会为每一个类生成一个字节码文件 - 运行是指Java解释器(
java
指令)将字节码文件翻译称机器代码执行并得到运行结果
- 编辑指编写代码,最终形成后缀名为
- JVM:Java Virtual Machine的缩写,Java虚拟机,用于运行Java字节码文件
- JRE:Java Runtime Environment的缩写,Java运行环境,作用:运行Java程序所必须的环境的集合,包含JVM标准实现以及Java核心类库
- JDK:Java Development Kit的缩写,Java语言的软件开发工具包 作用;JDK是整个Java开发的核心,他包含了Java的运行环境(JRE)和Java工具
三者的关系如下:详见 传送门
基础
- Java常见类型精度:
byte(1)<short(2)<char(2)<int(4)<long(8)<float(4)<double(8)
,需要注意的是Java中的char类型占两个字节,虽然和short一样都只占两个字节,但是无法将char类型自动转换为short类型,char类型相当于无符号整数。 - 对于一个整数常量,系统自动认为是int类型的,若一个整数后缀字母l或L,则被定义为long类型的长整数。对于一个实数,系统默认为是双精度型,即double类型,若需要把一个实数表示为单精度float类型,则需要在其实数后面加上字符f或F作标记。如2.54为双精度型实数,而2.54f则为单精度型实数。
- 当两个不同类型的操作数进行算术运算时,涉及到类型的转换,被转换为同一类型后再运算。类型转换分为系统自动转换和程序强制转化两种情况,从少字节的类型到多字节的类型、从整数类型到实数类型是自动转换,相反则需要强制转换。因此语句
float x=2.0;
会报错,因为2.0默认是double类型的,赋值给float类型的需要强制类型转换。(经测验从long到float是可以自动转换的) - 常用的数学函数被归类到系统类java.lang.Math中,它们都是静态(static)成员函数,通过类名和点运算符可以直接调用
- switch语句如果不break就会从上往下执行,如果遇到default任何条件都可以执行
- 类是对象的抽象,而对象则是类的特例,或者是类的具体表现形式
- 修饰类的修饰符只能为
public
final
abstract
和默认 - 封装提高了可重用性
- Java中静态变量可以不强制赋初值,如果要赋值可以在类体中完成。但是被
final
修饰的变量必须赋初值 - Java虚拟机中有一个
garbage collect
线程定期gc掉已经没有引用的对象。(就算一直实例化对象也不会溢出)
继承extends
- 使用
super
父类的构造函数只能在派生类构造函数的第一行调用(或者不调用,编译器会自动调用super(),即父类的无参构造函数,如果没有会报错) - Java重写条件比C++严格,在C++中只要和父类函数同名就会进行重写,但是Java中必须要参数列表相同,返回类型必须可以和父类返回类型兼容。否则就会认为是重载
- 静态方法不能被重写,当派生类中含有和基类同名的静态成员时将会进行覆盖(原来的还在,如果使用基类引用指向派生类对象的话访问到的还是原来的静态成员)
- Java也支持用基类引用指向派生类对象,同C++一样,基类引用只能访问基类中存在的属性
- Java重写以后自动实现多态,不像C++需要
virtual
修饰才能真正实现多态。使用基类引用指向派生类对象时调用重写函数将自动调用被重写后的函数 - 如果派生类和基类中含有相同名字的数据成员,可以在派生类中使用
super
进行调用,对于派生类对象需要进行类型转换才能访问基类成员。例如:
class father
{int x=1;
}
class son extends father
{int x=2;
}
class Test
{public static void main(String[] args){son s=new son();System.out.println(((father)s).x);}
}
运行结束后结果应该为1,需要注意的时需要两个括号。此时**重写后的函数访问这个同名成员x的时候访问的是派生类的,即使使用基类引用访问重写函数仍然调用的是派生类中的x,但是如果访问的是基类中没有被重写的函数修改的将会是基类中的成员属性。**可能有些晦涩,可以理解一下下面我写的测试样例:
package Test;class xx
{int t=0;xx(int _t){t=_t;}void eat(){t=1;System.out.println(t);}void change(){t=11;}
}
class xxx extends xx
{int t;xxx(){super(10);System.out.println(t);}void eat(int z){t=z;System.out.println(t);}void eat(){t=2;System.out.println(t);}
}class Main
{public static void main(String[] args){xxx x=new xxx();x.eat();x.eat(3);System.out.println(x.t);System.out.println(((xx)x).t);x.change();System.out.println(((xx)x).t);xx y=new xxx();y.eat();System.out.println(y.t);y.change();System.out.println(y.t);}
}
运行结果为:
抽象abstract/接口interface
-
接口的所有方法都是抽象的,每个成员变量默认具有
public
static
final
,并且只能在定义时被初始化。接口中的每个成员方法默认具有public
abstract
,同抽象类一样,接口不能被实例化。如果一个接口中没有包含成员变量的定义称这个接口为纯接口。如果一个类引用了某个接口,就必须实现接口中的所有方法。 -
Java中不支持多继承,但是支持实现多个接口
-
一个接口一个同时
extends
多个接口,即Java中的接口是支持多继承的。 -
当一个类实现一些互相继承的类的时候不用实现多次,每个方法都只用实现一次(默认就有C++中的
virtual
) -
经过测试,Java中接口中是可以含有非抽象方法的,不过方法需要是
private
修饰的。外部无法直接访问。变量必须为public static final
类型的,而且必须初始化。 -
需要注意的是在实现接口的时候如果接口的方法是默认的访问权限(
abstract public
)则在类中也必须为public
访问权限,因为不能缩小重写方法的访问权限。但是如果方法在接口中使用private
修饰则不需要(不能使用protected
修饰)。只是使用private修饰以后相当于在类中是重写而不是实现,而且接口中也必须实现private
方法 -
包声明语句
package
必须是Java程序文件的第一条语句,引入包语句import
必须在包声明语句和类定义模块之间 -
抽象类和接口的区别:
- 抽象类要被子类继承,接口要被类实现
- 接口只能做方法声明,抽象类中可以作方法声明,也可以进行方法实现
- 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量
- 接口式设计的结果,抽象类是重构的结果
- 抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别更高
- 抽象类可以有具体的方法和属性,接口中只能有抽象方法和不可变常量
- 抽象类主要用来抽象类别,接口主要用来抽象功能
异常Exception
- Throwable类是类库java.lang包中的一个类,该类不能直接使用。它派生了两个子类:Exception和Error。其中Error类表示恢复不是不可能但很困难的一种严重错误。所有的异常都继承
java.lang.Throwable
- 异常类一般是
Exception
的子类,类名通常以Exception
结尾 - 如果一个方法会抛出异常而我们不想在这个方法内部处理这个异常,我们可以在方法头后面加上
throws XXXException
,调用这个异常的方法必须处理这个异常,要么调用方也必须向上抛出异常。main函数的异常由JVM(java虚拟机)处理。 - 我们也可以自己定义自己的异常类,需要继承
Exception
,然后通过父类的构造函数传递信息,再通过printStackTrace()
方法在命令行打印异常信息在程序中出错的位置及原因,就会自动打印我们想要打印的信息(就是构造函数中的信息)。需要注意的是,我们在定义自己的异常类的时候必须定义一个private static final long serialVersionUID = 1L;
,可以参加下面这个例子:
package 实验五;public class IllegalAgeException extends Exception
{private static final long serialVersionUID = 1L; public IllegalAgeException() {}public IllegalAgeException(String message){super(message);}
}
finally
中的内容一定会运行,例如:
public static void main(String[] args)
{try {return;}finally {System.out.println("Finally");}//运行结果为Finally//System.out.println("Not return");编译错误,显示Unreachable code
}
try catch finally
的使用- 将遇见可能引发异常的代码包含在
try
语句块中 - 如果发生异常,将对异常的处理放入
catch
finally
可以没有也可以只有一个,无论没有发生异常,他总会在这个异常处理结构的最后运行,即使在try
块或者catch
块中用return
返回了,在返回前finally
总是要执行。如果没有catch
块则finally
块是必须的
- 将遇见可能引发异常的代码包含在
String类
- String类对象的值和长度都不能改变,称为常量字符串类
- Java中String没有字符串结束符
- 如果直接使用给字符串赋值常量字符串而不调用构造函数的方式,如:
String s1="China";
则会将“China”
放入内存池中,如果在后面再定义String s2="China"
则s1和s2指向同一个位置,他们的指针值都是相同的,但是如果使用构造函数例如String s3=new String("China");
的话则s3的值和s1、s2不同 StringBuffer
类可以使用append
方法在末尾添加其它字符串(也可以是其他基本类型,将会自动转换成字符串)
数组/Vector
- 对象数组在实例化以后相当于一个指针数组,每个元素仍然需要进行实例化。如果想要在数组创建的时候就进行实例化可以在后面加上花括号,里面是实例化的对象,但是这个时候我们不能指定数组的大小,需要编译器自己判断数组的大小。例如:
xx a[]=new xx[]{new xx(10),new xx(10),new xx(10)};//接上面的例子,xx是一个类
xx a[]={new xx(10),new xx(10),new xx(10)};//等价的写法
- 数组长度就是数组中包含的元素个数,当定义和创建一个数组后,数组长度值被自动保存到数组对象的成员变量length中,它是一个常量成员变量,被创建数组时自动初始化后,以后不允许改变它的值,只允许通过点运算符读取它的值。
char
类型的包装类是character
- 包装类常用的函数
boolean equals(Object obj)
,用于比较形参和当前对象是否相等String toString()
将当前包装类的值转换成字符串int compareTo(Object obj)
当调用对象大于参数对象时返回正数,小于时返回负数,相等时返回0
- 向量类
Vector
List
Set
等只允许保存对象类型,不允许保存基本数据类型 - Java 不支持重载运算符
- Java中for循环可以写成简化形式:
for(<元素类型> <变量名>:<数组或集合或向量>){<循环体>}//定义的变量将会遍历整个数组或者向量
- 需要注意一个小细节就是
String
中的length
是一个方法,而数组中的length
是一个常量整型成员
多线程Thread
- 创建线程有两种方法(想要了解更多可以戳 : 传送门)
- 继承
java.lang.Thread()
类,覆盖run()
方法,在创建的子类中重写run()
函数,加入线程所要执行的代码。 - 在类中实现
java.lang.Runable
接口,并实现run()
方法。实例化这个类以后就可以使用这个类实例化其他线程。通过这种方法我们可以实例化多个相同的线程。 - 当类还要继承其他类时使用第二种方法更加简便。实现线程以后我们就可以在主函数中使用
start
方法开启线程。 - Thread.start ()方法(native)启动线程,使之进入就绪状态,当CPU为该线程分配时间时,由JVM调度执行run()方法。
- 继承
IO/文件流
- Java输入流从结构上可以分为字节流和字符流(每个字符两个字节)
- 字节流的基础类是
InputStream
和OutputStream
这两个抽象类 - 若输入流对象创建失败(如对应的文件不存在时),将会引发异常
FileNotFoundException
,在程序中需要对其捕获和处理。创建输出字节流对象时,若指定的文件不存在,将会自动创建一个新文件 - 尽量手动关闭文件流
- 通过
String
的getBytes
方法将字符串转化为字符数组,可以用字符数组初始化字符串 - 文件类
File
的length
属性保存文件的字节大小 - 文件中的
\
应该写作\\
,否则会认为是转义字符 - 可以使用文件流实例化数据文件流
DataInputStream
类和DataOutputStream
类,然后就可以使用在这个流对象直接输入输出。一般方法为readXXX
或者writeXXX
- 如果需要频繁地读写磁盘,可以使用字节缓冲流
BufferedInputStream
和BufferedOutputStream
类读写文件。字节缓冲流对象将会建立一个内部缓冲区,当缓冲区满或者关闭字节缓冲流的时候一次性输出对应六,也可以使用flush
方法主动将缓冲区数据输入到流对象 - 我们还可以只用
PrintStream
类进行输出,同数据流DataOutputStream
一样,PrintStream
需要文件流进行初始化,然后就可以直接使用print
和println
函数 - System类中的in将会实例化为
BufferedInputStream
对象(虽然它本身是一个InputStream
类) - 关于字符流内容可以戳我的另一篇博客:传送门
GUI
- JPanel类的对象必须放在JFrame中才能可见
- 授权处理模型处理时间的一般方法:
- 对于某种类型的事件XXXEvent(例如点击鼠标),想要接收并处理这类事件,必须定义相应的事件监听器,该类需要实现与该时间相对应的接口
XXXListener
- 在该类中必须实现接口对应的方法(事件发生以后将会运行),不同的Listener有不同的方法
- 事件源实例化以后,必须进行授权注册该类时间的监听器,使用
addXXXListener
方法注册监听器
- 对于某种类型的事件XXXEvent(例如点击鼠标),想要接收并处理这类事件,必须定义相应的事件监听器,该类需要实现与该时间相对应的接口