Java速成笔记

恶补一下java来方便写作业,希望能用的上
一直有点抗拒这门语言,给我的感觉就是笨重麻烦,很难写一些很漂亮的做法

变量和类型

java是强类型语言,需要显式地声明变量的类型.变量的类型分为两种:

  1. primitive类型,包括:
    1. 整形:long int short byte,分别表示单字、双字、四字、八字
    2. 浮点数:float double,分别表示单精度、双精度
    3. bool和char,这里char很神奇是16bits的,看起来比C要现代一些
  2. 对象引用类型,这个看起来比较像没法作运算的指针

特殊地,数组也是对象,不管里面装的是啥.所以数组是new出来的.

在变量赋值中要求要严格一些,隐式类型转换必须是小范围转大范围(转换提升 implicit widening)
在java中小数默认是double,除非在数值末尾加上后缀'f'

事实上所有的对象都在带gc的堆上,因此这里的对象引用就真的只是引用了,对象本身并不在变量里

局部变量是没有初始值的(所以必须要初始化),实例变量(全局变量)的初始值是0

对象的相等需要调用equals()方法,指针和primitive变量的相等用==判断

类和对象

类是生成对象的模板,对象是数据和方法的抽象(集合).java是面向对象的高级语言,程序的操作主要通过对象之间的交互来进行.
下面这一段是不知道从哪里抄来的,预防自己以后忘了

In Java, a class is a definition of objects of the same kind. In other words, a class defines and describes the static attributes and dynamic behaviors common to all objects of the same kind.

An instance is a realization of a particular item of a class. In other words, an instance is an instantiation of a class. All the instances of a class have similar properties, as described in the class definition. For example, you can define a class called "Student" and create three instances of the class "Student" for "Alice", "Ah Beng" and "Ali".

The term "object" usually refers to instance. But it is often used loosely, and may refer to a class or an instance.

类的继承、接口

类分为抽象类和具体类

  1. 抽象类可以有抽象方法(无需实现函数主体),且无法创建抽象类的对象。任何继承了抽象类的类都要覆写抽象方法
  2. 具体类就是一般说的类

子类可以继承父类的public和protected方法和变量

判断A是不是B的子类可以问问自己“A是一个B吗?”,注意区分"has a"与"is a"的区别

类的继承只能形成树形结构,即继承的父类是唯一的.为了方便后期维护屎山(主观看法)还引入了接口类

  1. 接口只能有抽象方法,且无法创建对象。接口可以随意“继承”(这里叫接口的实现)。引入接口的目的会在下面多态谈到

利用super关键字调用父类的方法和变量

类的产生和消亡

首先要区分Scope和Life

  1. Scope指的是作用域,比如说foo()方法中的变量x在foo()调用了bar()之后无法访问,但仍然活在栈上
  2. Life指的是生命周期,比如说foo()方法结束后,其内部的局部变量都消亡。Java中对象消亡当且仅当没有指向它的指针了

类位于堆上,类的产生会调用构造函数

在调用类A的构造函数时,会自动调用A的父类的构造函数,并且这个过程是递归的。这个过程称为构造函数链

在默认情况下编译器会在类的构造函数的最前面加上super()来调用父类的默认构造函数,当然你可以自行调用带参数的父类的构造函数比如说super(123)
当然也可以在某类的最前面用this()调用该类的其它构造函数。thissuper是只能二选一的

类的消亡是完全由GC完成的,内存不需要自己管理(这一点还算不戳)

静态方法和静态变量

实际上就是java的OO太浓,在只需要全局函数(例如说sqrt()round()ceil()等函数,pi等常熟之类的)的情况下,如果仍然创造类才能调用某个方法就很sb,于是就引入了静态方法(静态变量)的做法。实际上java的实现是通过声明Math类的构造函数为private来实现的。用抽象类来实现也是可以的

静态方法不会构造对象,因此不能调用实例变量,也不能调用非静态的方法

静态方法虽然可以通过引用变量调用,但还是推荐用类名调用静态方法来避免歧义

类的所有对象共享它们的静态变量,静态变量只会在类被加载时被初始化。类的加载通常在第一次类的实例化时进行

用final修饰的变量不能被改变和覆写,方法也是一样(类和形参也可以这么做!)

final变量的初始化:

public class Foo {
	public static final Pi = 3.14; // method 1
	public static final Pj;
	static {
		Pj = 3.15; // method 2
	}
}

多态

这个很强了,算是OO前期的重点,用起来也非常方便

大概意思就是说,一个类(接口)的指针可以指向其所有子类的对象。也就是说对于更高层次抽象的方法,可以使用更高层次抽象的类来进行处理,这样以后传入其它具体的子类的时候就可以复用代码(在java里到处都能看到复用代码...)

需要注意的是,编译器通过指针的类型而不是对象的实际类型来判断能使用哪些方法和变量,这个也是比较好理解的(在编译期是没法知道指针究竟指向了什么类型的对象的...).并且还有一点比较重要,考虑下段代码

class A {
	public void fA() {
		System.out.println("fA");
	}
}

class B extends A {
	public void fA() {
		System.out.println("fB");
	}
}

public class Test {
	public static void main(String[] args) {
		A a = new A();
		B b = new B();
		A pb = b;
		a.fA();
		b.fA();
		pB.fA();;
	}
}

它的输出结果是

fA
fB
fB

也就是说,即使我们用A的指针指向B类的对象并调用方法,调用的也是B覆写过后的方法

从另一个角度看,也就是即使指针也可以隐式地类型转换,并且和primitive数据类型一样只能提升转换(类比就是从小范围到大范围,从具体到抽象),从这一点来看java的设计还是很一贯的。而java强大的一点就在于所有类型(除了6个primitive之外)都继承自Object类....

异常

算是第一次正式接触异常和处理的概念...

异常也是Java的类,在出现错误时方法会抛出异常来通知调用方出现了错误。抛出异常的方法需要在方法的声明处声明它会抛出哪些异常,例如

public void thrower() throws MyException {
	// blah blah blah
}

同时,调用一个会throw异常的方法一定要做异常处理,例如

try {
	this.thrower();
} catch (MyException ex) {
	// handler
} catch (OtherException ex) {
	// other handlers
} finally {
	// do something after all cases.
}

Exception仍然是支持多态的,也就是可以用父类的Exception接受子类Exception的对象。
而java处理异常的流程是:接到了异常,从上往下找到第一个能指向这个异常对象的处理方法,进入处理。因此最好的安排方式是子类异常先于父类异常处理(否则父类肯定能先catch到异常,我们细分处理就没有意义了)。

上面的代码中finally是无论如何都要执行的部分。而有一点比较蛋疼就是说如果在某个catch中执行了return,那么仍然是执行完finally再执行return的

考虑如下代码

public class TestClass {
	int j;

	public void g(int x) throws AException, BException {
		if (x == 1) {
			throw new AException();
		} else if (x == 2) {
			throw new BException();
		}
	}
	
	public Integer f() {
		Integer a = 0;
		try {
			g(1);
		} catch(Exception ex) {
			return a;
		} finally {
			System.out.println(++a);
		}
		return -1;
	}

	public static void main(String[] args) {
		TestClass d = new TestClass();
		System.out.println(d.f());
	}
}

执行之后可以发现输出的结果是先1后0,也就是在执行finally之前return值就已经确定了,但仍然是先finally再结束这个方法....我直接疑惑了

实际上catch也不是必须的。如果你觉得某些异常不应该在当前方法内被处理,那么就可以在当前方法的声明处表明它仍然会throw这个异常,然后不catch就好了

例如说

void f() throws A, B {
	// blah blah
} ;

void g() throws A, B {
	f(); // to avoid a throw, we duck
}
posted @ 2021-06-19 15:06  jjppp  阅读(284)  评论(0编辑  收藏  举报