运算符
原码、反码、补码 —— 假设机器字长8位
解决计算机花大功夫确定符号位问题 → 让 符号位 参与 运算 (补码、反码符号位都可参与运算)
计算机运算只进行 加法 运算
- 最高位存放 符号位 → 0代表正、1代表负
0000 0001 → 机器数、+000 0001 真值
- 原码:
8位二进制数实际值范围:[-127, +127]
- 反码: → 解决加法运算、符号位参与运算
- 不足: 并未解决 +0、-0两个都表示0的细节
- 正数反码是本身,负数的反码:符号位不变,其余位取反
+1:原码:0000 0001 反码:0000 0001
-1:原码:1000 0001 反码:1111 1110
+0:原码:0000 0000 反码:0000 0000
// 导致有两个反码表示0 0000 0000( +0 )、1111 1111( -0 )
反码进行运算:+0 + +0 = 0000 0000 ( +0 )
反码进行运算:+1 + ( -1 ) = 0000 0001 + 1111 1110 = 1111 1111 → 原码:1000 000 ( -0 )
- 补码: → 在补码的基础上,解决双0问题、且其中一个0能多表示一个负数
- 正数补码是本身,负数的补码:符号位不变,其余位取反,再加1( 即反码的基础上加1 )
+1:原码:0000 0001 补码:0000 0001
-1:原码:1000 0001 补码:1111 1111
-127:原码:1111 1111 补码:1000 0001
+1 + ( -1 ) = 0000 0001 + 1111 1111 = 0000 0000 → 原码:0000 0000
-1 - ( -127 ) = 1111 1111 + 1000 0001 = 1000 0000 → 无原码,补码1000 0000 即表示-128
各种运算符
- ==: 判断两对象内存地址是否相等
- instanceof: 判断左边的对象是否是右边类的实例
由这个语句从中也可以得知:基本变量不是对象实例,基本变量的数组是对象实例
String a = "Hello"; //直接赋值字符串,静态绑定,编译器已经确定好放置在常量池 - 常量池的变量 == 比较的是值
String b = new String("Hello"); // 动态绑定,运行期确定地址
if (b instanceof String) {
System.out.println("equal");
}
移位: 计算机中一个数的位数是固定
例如 → 整数:4字节、32位
符号位永远是左边的第一个
- <<: 补码整体左移动,右边补0
- >>: 补码整体右移动,左边补符号位
- >>>(无符号移动): 补码整体右移动,左边补0
int a = -5;
println(Integer.toBinaryString(a) ) // 输出a的补码
int b = a<<1 //十进制结果 -10
int c = a>>1 //十进制结果 -3;
//因为左边补0,导致补码成为正数
int d = a>>>1 //十进制结 2147483645
二进制位与运算符:&
即把字符转换成二进制,对应位的二进制数进行 与运行
int a = 1; // 二进制数:001
int b = 5; // 二进制数:101
println( a & b ) // 二进制与运算: 001 输出:十进制1
控制执行语句
foreach : 由 for 组成而来的:for( x : 数组)
跳转的程序控制:
- continue:跳出当前的迭代
- break : 跳出循环
goto中的标签跳(label: + 直接迭代(循环)语句)
- continue label : 跳转到llabel位置,再次进入迭代
- break label: 跳转到label位置,并且不再进入 label : 紧随的迭代(循环)
switch语句:多路选择,不过选择因子必须是 int 或者 char
一旦匹配到”对应的因子“,如果没有break; 会接着执行后面的选择因子语句
调用
重载 :
①如果重载的数据类型小于或者大于,数据类型会被提升或者窄化。
②只要方法名同有无返回值(int 、 void)并无影响。
方法的调用:
- 对象.方法名( 参数 )→ 类名.方法名( 对象名{引用、位置},参数 )
- 方法调用时参数的类型判断是发生在编译前的,故泛型要检验时要放在形参上,而不是在方法内部进行转换(实质一点都没有转换,运行时已经抹除泛型)
这是编译器调用方法时的隐形操作。
Class Father<T> {
List<T> list = new ArrayList<T>();
public void add(T a) { //在编写调用方法时,会检验类型
list.add(a); //一旦调用成功必定是 初始化时的 T类型
)
}
注意 public void add(Object a) {
list.add( (T)a ) //写的是强制转换,实质并没有强制转换,因为代码在运行时,有关T的信息完全被抹除
}
this关键字 : 方法内使用,表明这是当前调用方法的对象。
构造函数(构造器)可以调用其他重载了的构造器函数→新对象的初始化。 不过只能调用一个
static成员函数:不能调用非静态的成员方法。因为静态方法可以只用类名调用,而 非静态的方法一定要有指定类型的对象调用才能。
当对象的引用 = null System.out.println(引用) 依然会调用对象的toString方法进行转换。
方法调用的参数栈: 当调用语句时将参数、返回地址入栈;当遇到函数结束时退栈返回
public class Test {
public static void p(int n) {
if(n>1 && n%2==1) {
p(n-1);
}
System.out.println(n);
if (n>1 && n%2==0)
p(n-1);
}
public static void main(String[] args) {
Test.p(5); //参数入栈顺序 p(5) → p(4) → p(3) → p(2) → p(1)
// 出栈顺序:入栈顺序的反方向,先进后出
//输出:4 2 1 3 5
}
}
native关键字
- 不是java原生的方法,用其他语言实现的函数
- JNI( java native Interface ): 可以允许 本地调用 其他语言编写的方法,类库( 打包成DLL使用 )
- 只有该关键字修饰的方法 被调用,相关被调用的DLL实现才会被载入内存
垃圾回收(清理)
垃圾回收器:只会释放new创建的(无任何引用)对象,如果由非new的内存只能由 终结函数finalize() 手动释放。
垃圾回收模式:
- 引用计数: 引用接近对象时+1,引用为null或者离开作用域时-1.计数为0就释放对象。缺点:循环引用,导致内存泄露
- 自适应: ①停止-复制(用于碎片多)②标记清理(用于垃圾少)③分代(块的引用次数、感觉类似标记)
finalize() : 调用它后,只能等到 下一次垃圾回收时刻 才释放内存,如果垃圾回收一直无动作,则该准备终结的内存一直不会释放。可根据对象情况自己重载系统的finalize();
class book {
boolean checkout = true;
protected void finalize() {
if(checkout) {
System.out.print("你还有书未登记");
}
else
System.out.println("你的书已经全部登记");
}
}
public class lx {
public static void main(String[] args) {
new book(true); //无引用的对象 垃圾回收一定会认为后面用不到,从而准备回收
System.gc(); //强制的执行 (new无引用的对象) 终结工作( finalize) ,垃圾回收器也会开启。
}
}
初始化
- 数组进行初始化时有 三种方式
Integer[] a = new Integer[2];
Integer[] a = new Integer[] { 元素 };
Integer[] a = { 元素 }}
- 子类进行初始化父类的构造器需要用到 super() —— 只能出现在子类构造器中的第一行
- 常量final类型的变量名按照惯例大写、下划线
- 调用时方法的参数必须是初始化(即有 " 值 ")
- 实例化对象时,类内部执行顺序:1.一定会初始化静态变量 2.类内无名执行字段 3.有对象初始化(基本变量初始化(一定会)、其他如类内变量对象需手动初始化,系统不会帮你赋值 → 构造函数)(继承的对象则先父类后子类)
静态代码块、字段 在一个类有且只能执行一次
- 形参:Object… args 等价于 Object[] args
- 如果java中有多个在不同包名的同名类,无定义包名初始化的对象优先用当前包的类 → java系统类 如果初始化有包名限制则优先用有包名规定的类来初始化 或者 导入import所用类
前者调用时的参数可以不像后者那样初始化一个数组,可写成一个一个的对象
static void print1 (Object[] args) {
for (Object item : args) {
System.out.println(item);
}
}
static void print2 (Object... args) {
for(Object item : args) {
System.out.println(item);
}
}
print1( new Int[] {1,2,3,4} );
print2(1,2,3,4) //这两条调用是等价的,peinr2的参数系统会自动的转为数组
类的初始化:
- 类定义字段的时候初始化(对类内的对象字段以及数组不会初始化)
- 类的构造器中
- 将要使用时进行初始化(不再构造器中初始化),即 惰性初始化
深/浅拷贝
- 浅复制: 类实例的属性 (元数据:值传递、类内对象:引用(地址传递)
- 深复制: 类实例的属性 (元数据:值传递 类内对象:新的对象,而不是传引用)
class Subject {
String subject;
Subject(String subject) {
this.subject = subject;
}
}
class Student implements Cloneable{
String name;
int age;
Subject sub;
Student(String name, int age, String subject) {
this.name = name;
this.age = age;
sub = new Subject(subject);
}
public Object clone() { // 浅拷贝
try {
return super.clone();
//深拷贝
// Student a = new Student( name , age, sub.subject )
// return a;
}catch(Exception e) {
return null;
}
}
}
Student stu1 = new Student("lrc","10", "数学");
Student stu2 = (Student)stu1.clone();
stu1.age = 10; //并无改变元数据 stu2 的值
stu1.sub.subject = "英语" //因为是引用所以改变了 stu2.sub.subejct 的值,同一引用
复用类(组合、继承、代理)
组合 : 增加功能而不是暴露接口(车对象与轮子对象的关系),类内置组合对象一般为private (is - a)
继承 : 会把父类的所用方法继承到子类 (have - a)
继承表达行为间的变化,字段(类内对象(组合)、基本变量)表达状态上的变化
继承不能降低可视的成员函数的访问权限
类成员能够降低访问权限,实在是两个不同的变量,虽然他们变量名一样 —— 因为有 super能访问父级的类成员
class Father {
int a = 10;
public void cout() { }
}
class Son extends Father {
private int a; //降低了继承的类成员访问权限,不过实质是两个不同的变量。
void cout() {} // 出错,权限从 公有 → 包权限 故会报错
public void cout() {
System.out.prntln( a + " " + super.a ); // outpur:0 10
}
代理 : 某类引进需要代理的对象,在类中定义方法调用对象的成员函数
能自定义的引进代理对象的成员方法,而不像继承一样一股脑的继承所有祖先类的方法
class a {
void up(int velocity) {}
void down(int velocity){}
}
public class b {
private a daiLi = new a(); //屏蔽代理对象dalLi的细节
void up() { daiLi.up(); } //类b代理a类的对象,自由的引进a类的方法,重新包装
void down() { daiLi.down(); }
}
调用:
- 父类的静态函数用 父类的类名调用
- 父类的普通成员函数 用关键词 super 调用
方法签名: 方法名 + 形参列表
子类实例初始化顺序: 先祖先类 → 子类字段 → 子类构造函数
@override注解:帮检查重写父类方法的正确性,例如子类重载的方法名是否与父类同,以及返回类型等 可当关键词用
向上转型: 即子类对象转成父类的对象(注父类是子类的子集) - 意味着子类有的父类无的接口被屏蔽
向下转型一定要 强制转换,要不然提示错误。
注意:只有引用本身是子类的对象实例,才能向下转型
向上转型并不能调用父类的private(私有方法)—— 可用 @Override注解验证
public class amphibian {
void eat() {
System.out.println(" 我是两栖动物 ");
}
static void cout (amphibian a) { //向上转型入口
a.eat();
}
class public frog extends amphibian{
@Override void eat() {
System.out.println(" 我是青蛙(两栖动物)");
}
public static void main(String[] main) {
frog f = new frog();
f.eat(); //输出:我是青蛙(两栖动物)
amphibian.cout(f); //输出:我是青蛙(两栖动物)证明向上转型如果调用的是父类与子类重写同名的方法,则还是会调用子类重写的方法。
final(不可更改)数据
变量即是 static 又是 final 则变量名 大写且下划线分隔
- 对于基本数据类型(不可改变值,需要定义时要初始化)
- 对于对象,只能保证引用(对象地址)不变,其对象里面的内容可以更改
- 允许空白域(即声明了一个final变量可不赋初始值,不过必须在构造器中初始化
- 可应用于在方法形参列表中,即不能改变变量的值,引用的值(地址)
- 锁定方法:即内嵌方法(复制方法代码到当前调用的位置-用方法代码的复制体)取代 转到方法地址(压栈,跳回原地址-用原方法代码),不过hotspot技术会优化内嵌代码
private方法隐含final,即不能重载、重写
- 锁定类( 定义类时修饰final ):即该类不能被继承