Java 跟 C# 有些地方很相似,要注意串联。
1 关于概念
Java 程序实现的具体流程:
源文件(.java) => 编译器 => 字节码文件(.class) => 解释器 => Program(具体平台的机器指令)
- JVM(Java Virtual Machine),即 java虚拟机,是java平台无关性实现的关键。
- JDK(Java Development Kit),即Java 语言的 软件开发工具包,开发阶段软件的编译与执行全依赖它了,其中有两个重要的组件(命令):
javac
:编译器(.java
=>.class
)java
:运行编译后的 Java 程序 (.class
)
- 且JDK 包含:
- JRE
- 开发工具集(如
Javac
编译工具)
- JRE(Java Runtime Environment),即 java 运行环境,其内容包含:
- JVM
- Java 核心类库和支持文件(JavaSE 标准类库)
JRE 与 JDK 的区别,前者面向使用者,后者面向开发者
Java平台有三个,分别是:
JavaSE
:java 标准版,多用于桌面程序JavaEE
:java 企业版,包含JavaSE
,多用于 Web 程序JavaME
:java 微型版,多用于移动设备
2 关于数据类型
Java 的数据类型分为 基本数据类型 和 引用数据类型。
其中含有8种基本数据类型,它们又分为:
- 数值型,数值型又分为:
- 整数类型(包含正负):
byte
(内存长度 8 bit)short
(内存长度 16 bit)int
(内存长度 32 bit)long
(内存长度 64 bit)
- 浮点类型(包含正负):
float
(内存长度 32 bit)double
(内存长度 64 bit)
- 整数类型(包含正负):
- 字符型(
char
):使用 单引号,内存长度 8 bit - 布尔型 (
boolean
):内存长度 8 bit
整数类型的 八进制 描述方式是以 0
开头的,十六进制 描述方式是以 0X
或 0x
开头的。
因为计算机并不能实际表示某些浮点数(如
0.1
),所以浮点数之间的直接比较是不靠谱的,它们的比较应该利用其差值小于某个临界值来判断,如Math.abs(x-0.1) < 0.00001
2.1 关于字面量
当赋值字面量的结尾添加 l
或 L
字母的时候,该数值表示一个 long
类型,此时如果赋值给一个 int
类型的变量就需要强转换,如
int int1 = (int) 123L;
即声明 long
类型时,最好在字面量后添加后缀字母,同理的还有 float
类型字面量结尾添加 f
或 F
,double
类型字面量结尾添加 d
或 D
。
不过此时要注意的是赋值时,浮点类型的字面量默认为 double
,即 float float1 = 1.2;
这是会报错的,因为上述代码的意思是将一个 float
类型的变量地址指向 double
类型的,所以此时就要显式的转换类型,或者直接修改字面量,如 float float1 = 1.2f;
2.2 关于数值的比较及转化
有时候整数尽管可以隐式转化为浮点数,但依旧可能会发生 精度缺失 ,比如:
long long1 = 12345678972555555l;
double double1 = long1;
与不同的整数类型比较一样,浮点数与整数进行比较,只要 数值相同,就会返回 true
,比如:
float f = 5;
long l = 5;
System.out.println(f == l);
3 关于对象
##3.1 关于对象的创建
一个对象的创建包括 声明对象 和 实例化对象。
声明对象:在内存** 栈 空间**里开辟一个区域,但还没指向实际内容
实例化对象:在内存的 堆空间 里开辟一个区域,在将 栈空间 的内存地址指向这个区域。
3.2 关于修饰符
类的修饰符只有:
public
,abstract
以及final
;
构造函数的修饰符只有:public
,protected
,private
以及默认值
3.2.1 关于访问修饰符
public
:最开放protected
:在同包的任意类中都可以访问,在不同包就只能是其继承类可以访问- 默认值:只能在同包中访问
private
:只能在本类中访问
3.2.2 关于其它修饰符
final
修饰符:如同名字的意思一样,当 final
修饰符修饰方法内的局部变量时,此时就像声明一个常量,无法修改,当该变量是一个引用类型时,该变量的内存指向地址无法改变。假如静态属性使用了 final
修饰,此时不在声明中赋值或者在 **静态代码块**中赋值就会报错
3.3 关于构造函数
- 先初始化字段,再执行构造函数
- 构造函数没有返回类型(即
void
也不能添加),只要类声明了一个构造函数,无论是否带参,系统默认的无参构造函数就会自动取消 - 重载构造函数之间的相互调用可以使用
this
关键字,而且该语句必须放置在该方法块中的第一行 - 可以使用
super
调用父类指定构造函数,而且该语句必须放置在该方法块中的第一行,所以super
以及this
是不能共存的,同时super
在子类的方法中调用时指明的是一个父类实例。 - 当构造函数的修饰符是
private
的时候,说明只能在本类中使用该构造函数来创建对象,在其它类中无法调用得了该构造函数
3.4 关于构造代码块
在类的内部可以添加 花括号{}
,来创建 构造代码块,使用 static
修饰花括号即可创建 静态代码块,其中在一个对象的创建过程中,静态代码块的执行顺序最早,然后到构造代码块,最后是构造函数。
假如静态属性使用了
final
修饰,此时不在声明中赋值或者在 **静态代码块**中赋值就会报错
3.5 关于继承
继承对象的初始化顺序是:
父类静态构造方法块 =>
子类静态构造代码块 =>
父类构造方法块 =>
父类构造函数 =>
子类构造代码块 =>
子类构造函数
子类的创建必须调用父类的一个构造函数,默认不显式声明的状况下(使用 super
)是调用父类的无参构造函数,如果此时父类没有无参构造函数,而且子类的构造函数中有没有显式地调用父类的其它构造函数,就会报错。
3.6 关于重写与重载
方法重载条件:
- 同一个类中
- 方法名相同,参数列表不同(顺序,个数,类型)
- 与返回值、访问修饰符,参数名称无关
原则上,重载的方法应该完成相同的功能,尽量不要依靠参数顺序的不同实现重载,而且重载的方法的返回值尽量相同
方法重写条件:
- 在继承的子类中
- 方法名,参数列表相同,返回值相同或是子类
- 访问修饰符范围不能比父类小
- 与参数名无关
3.7 关于向上转型与向下转型
- 向上转型(隐式转型,自动转型):父类引用指向子类实例,此时该父类变量可以调用子类重写父类的方法以及父类的派生的方法,无法调用子类的特有方法。
- 向下转型(强制转型,自动转型):子类引用指向父类实例,必须要显式转换,此时可以调用子类特有的方法,要注意想要 实现向下转型的前提是先向上转型,也就是说该父类实例是由向上转型之后得出的结果,正常地声明一个父类实例,是不能实现向下转型的(不然如果父类Fruit实例出来的对象是Orange,Orange当然不能强制转成Apple,所以说父类只有该子类对应的实例才能强转)。
- 判断一个能否向下转型,最简单的方法就是使用
instanceof
运算符(注意是运算符,返回是布尔值)
引用变量的声明类型与其实际类型可能不同,实际对象的方法调用总是指向实际类型,因为 Java 的方法调用是 基于运行时 的动态调用;
而多态是指针对某个类型方法的调用,其真正执行的方法取决于运行时的实际类型的方法
3.8 关于抽象(abstract
)
面向抽象编程的本质是:
- 上层代码只负责定义规范
- 不需要子类也可以实现业务逻辑
- 具体的业务逻辑由不同的子类实现,调用者并不关心
使用了 abstract
修饰符的类叫做抽象类,抽象类 不能直接使用 new
的方式实例化,可以先让子类重写了它的抽象方法后实例化子类,在进行向上转型,从而达到实例化的效果。
抽象方法不允许有方法体,子类必须重写该方法,否则子类也要同时声明为抽象类。
3.9 接口
当一个抽象内全是抽象方法的时候,就可以把这个抽象类转换为接口,接口定义了某一批类所需要遵守的规范,默认访问修饰符 public
。
在JDK1.8中,接口可以包含常量,静态方法,使用 defualt
关键字还可以使接口带上方法体,当接口的默认方法与实现类的方法同名时使用实现类的方法,但如果是属性值同名就会报错了。
接口与抽象类的区别
接口 | 抽象类 | |
---|---|---|
继承 | 可以 implements 多个接口 |
只能继承一个接口 |
字段 | 可以声明实例字段 | 不能声明实例字段,只能声明 final 常量,默认声明字段带有 final 修饰符 |
抽象方法 | 可以定义抽象方法 | 可以定义抽象方法 |
非抽象方法 | 可以定义非抽象方法 | 可以使用 defualt 修饰符定义抽象方法,以达到类似定义非抽象方法的效果 |
3.10 总结代码
写了一下测试代码总结一下:
父类:
package cn.Seiei.test;
public class Father {
public Father(String arg) {
System.out.println("父类构造函数");
}
{
System.out.println("父类构造方法块");
}
static {
System.out.println("父类静态构造方法块");
staticData = 123;
}
public final static int staticData; // 使用了final修饰,假如不在声明中赋值或者在静态代码块中赋值就会报错,静态属性应该要在静态代码块中完成初始化
public String data = "父类实例属性";
public static String staticData2 = "父类静态属性";
public static String staticData3 = "父类静态属性2";
public static void say() {
System.out.println("父类静态方法");
}
public void tell() {
System.out.println("父类实例方法");
}
}
子类:
package cn.Seiei.test;
public class Son extends Father {
public Son() {
super("test");
System.out.println("子类构造函数");
}
{
System.out.println("子类构造代码块");
}
static {
System.out.println("子类静态构造代码块");
}
public static void say() {
System.out.println("子类静态方法");
}
public void tell() {
System.out.println("子类实例方法");
}
public void testForSuper() {
String test = super.data; // super 指明是一个父类的实例,不能在静态方法中使用
System.out.println(test);
}
public static String staticData2 = "子类静态属性";
public String data = "子类实例属性";
public void sonSay() {
System.out.println("子类特有的方法");
}
public static void main(String[] args) {
// 检测执行顺序
System.out.println("检测执行顺序:");
Son son = new Son();
System.out.println("====================================");
// 继承后静态属性的共用情况
System.out.println("继承后静态属性的共用情况:");
System.out.println(Father.staticData3);
Son.staticData3 = "子类静态属性2";
System.out.println(Son.staticData3);
System.out.println(Father.staticData3);
System.out.println("====================================");
// 继承后静态属性的重写
System.out.println("继承后静态属性的重写,实例属性的重写:");
Father sonToFather = son; // 向上转换
System.out.println(sonToFather); // 此时输出还能看到 Son 的信息
System.out.println(sonToFather.staticData2); // 静态属性
System.out.println(sonToFather.data); // 实例属性
System.out.println("====================================");
// 继承后静态方法的重写,实例方法的重写
System.out.println("继承后静态方法的重写:");
sonToFather.say(); // 静态方法
sonToFather.tell(); // 实例方法
System.out.println("====================================");
// 继承后子类方法的中调用父类
System.out.println("继承后子类方法的中调用父类方法,属性:");
son.testForSuper();
System.out.println("====================================");
// 父类强制转换为子类调用子类的方法
System.out.println("父类强制转换为子类调用子类的方法:");
Father father = new Father("test");
System.out.println("正常声明的父类实例能否向下转型:" + (father instanceof Son));
System.out.println("经过装箱的父类实例能否向下转型:" + (sonToFather instanceof Son));
Son fatherToSon = (Son) sonToFather;
fatherToSon.sonSay(); // 此时可以调用子类特有的方法
}
}
#4 关于异常
参考文章《java异常—检查异常(checked exception)和未检查异常(unchecked exception)》
关于 Eclipse
-
初次配置时,最好先到
windows -> preferences -> workspace
中改写Text file encoding
为utf-8
编码以及勾选refresh using native hooks or polling
以便其它编辑器修改后,Eclipse 自动更新,同时酌情地改写windows -> preferences -> java -> compiler -> Error/warning
设置,以便编辑器更好提示错误警告 -
debug快捷键:
F6
:逐步调试F8
:从一个断点执行直接跳到下一个断点执行F5
:跳进断点的方法内部调试,F7
跳出
-
文档注释:
/**
+ 回车;点击@
符号可查看添加注释参数;使用文档注释后,在使用输入该方法时,鼠标悬浮该方法名字会出现提示 -
alt
+/
:可以查看指示菜单 -
Run configurations
:命名行运行,此时可以添加命名行参数
杂
-
&&
运算符与&
运算符的区别,&&
是短路运算符 -
数组是对象,对象的属性拥有默认值(局部变量没有默认值),如整型默认值为
0
,对象默认值为null
,String
类型也是对象所以默认值也是null
,浮点默认值0.0
,布尔型默认值false