Java快速入门
0 背景
为了快速复习和捡起java方面的知识,力求段时间内可以看懂java代码。抛去java中细枝末叶的知识点,只学习java的主干知识,于是阅读了《Head First of Java》(很干练,不像《Java核心技术》或《Java编程指南》等书籍,完全可以像字典一样使用),本文就是对此书的总结。如果想熟练的使用Java进行网页的编写,可以结合实际项目,在实际项目中边实践边学习。
1 变量
1.1 primitive类型
boolean char byte short int long float double
干好与下面的句子中的每个字母相对应:
Be Careful! Bears Shouldn’t Ingest Large Furry Dogs
1.2 primitive封装类
Java为者8中基本接力都提供了类的封装,分别为Boolean Character Byte Short Integer Long Float Double
出现的意义(实现万物皆对象):
- 1)类型之间的转换;
- 2)泛型中使用;
- 3)强制类型转换;
- 4)集合中使用。
基础类型和封装类的区别:
-
1)传递方式不同
- 基本类型是按值传递,而封装类型是按引用传递的。int是基本类型,直接存放数值;Integer类会产生一个对象引用指向这个对象。
-
- 存储位置不同
- 基本类型存储在栈中,封装类的引用存储在栈中,而值是存在堆中。
-
- 比较方式不同
- 基本型比较使用“==”,封装型比较使用equals();对封装型进行赋值时,不要通过基本型去复制(
== 进行比较的是地址,equals() 比较的是其中的值)【即,使用== 来比较两个primitive主数据类型,或者判断两个引用是否引用同一个对象;使用equals() 来判断两个对象是否在意义上相等(如两个String对象是否带有相同字节组合)】。
- 4)初始值的不同
- 封装类型的初始值为null,基本类型的的初始值视具体的类型而定,比如int类型的初始值为0,boolean类型为false;
测试代码(参考):
public class Test{
public static void main(String[] args){
Integer integer1=new Integer(100);
Integer integer2=new Integer(100);
System.out.println(integer1==integer2);
Integer integer3=100;
Integer integer4=100;
System.out.println(integer3==integer4);
Integer integer5=1000;
Integer integer6=1000;
System.out.println(integer5==integer6);
}
}
结果解释:
- false:new在堆中开辟空间 1与2的地址不同,而==仅对地址进行比较,如果使用System.out.println(integer1.equals(integer2));则输出结果为true,因为equals()函数对值进行比较
- true:100 自动装箱在Integer的缓冲区中,直接返回缓冲区对象,地址相同,为true
- false:1000已经超出100的缓冲区了,会分配新的地址,所以使用==进行比较时结果false
1.3 保留的关键字
boolean byte char double float int long short public private
protected abstract final native static strictfp synchronized transient volatile if
else do while switch case default for break continue assert
class extends implements import instanceof interface new package super this
catch fi nally try throw throws return void const goto enum
1.4 只有值传递,没有引用传递
1.5 格式化
Format()格式化参数:%[argument number][flags][width][.precision]type
- 1)
%符号 : 把参数放在这里,若在%后加上,数字将以逗号分隔开,如476,578.10234 - 2)
[argument number] : 如果要格式化的参数超过一个以上,可以在这里规定是哪一个。 - 3)
[flags] : 特定类型的特定选项,例如数字里加逗号或正负号 - 4)
[width] : 宽度,最小的字符数,不足补齐,可超过此宽度 - 5)
[.precision] : 精确度 - 6)
Type : 一定要指定的类型标识
例如:
对应 | | | | | |
---|
% | [argument number] | [flags] | [width] | [.precision] | type | format("% | 1$ | , | 6 | .1 | f", 42.2) |
例子:
public class Test{
public static void main(String[] args){
int one = 123;
double two = 678.12;
String s= String.format("The rank is %2$,d out of %1$,.2f", two, one);
System.out.println(s);
}
}
使用< 来告诉格式化程序重复利用之前的参数:
Date d = new Date();
String.format("%tA, %<tB, %<d", d);
1.4 autoboxing与Unboxing
Java 1.5开始出现的特性,可以完成自动的装箱和拆箱(让编译器来自动完成在基本类型和它们的包裹对象之间的转化工作),可以显著提升速度(约5倍)。
未使用autoboxing 的代码:
public class Test{
public static void main(String[] args){
ArrayList lisNumber = new ArrayList();
lisNumber.add(new Integer(3));
Integer one = (Integer) lisNumber.get(0);
int intOne = one.intValue();
System.out.println(intOne);
}
}
等价于下面的代码(autoboxing ):
public class Test{
public static void main(String[] args){
ArrayList<Integer> lisNumber = new ArrayList<Integer>();
lisNumber.add(3);
int intOne = lisNumber.get(0);
System.out.println(intOne);
}
}
Auto-Unboxing的局限 Auto-Unboxing的机制则有这样一个局限——只能把包裹类对象往它们对应的基本类型(以及容纳范围更广的类型)上转化。
类似这样的代码是不能工作的,尽管32并未超出byte所能表示的范围(参考文章):
-
1,不能同时进行Auto-Unboxing和强制向下转型 Integer i = new Integer(32); System.out.println((byte) i);/* 编译时出错 */ 这是因为编译器并不认可同时进行Auto-Unboxing和强制向下转型的操作,所以这个转化无法进行。如果一定要进行这种操作,需要手工补充一次转型: -
2 ,需要先作Unboxing,再强制向下转型 Integer i = new Integer(32); System.out.println((byte)(int) i); 不过同时进行Auto-Unboxing和强制向上转型的操作是没有问题的,所以下面的代码工作得很正常: -
3 ,可以同时进行Auto-Unboxing和强制向上转型 Integer i = new Integer(32); System.out.println((double) i);
2 类和对象
类的权限:private ,default ,protect ,public (从左到右受限程度越小)。
private 不会被继承,public 会被继承。
对象是类的实例,类是对象的模版。
2.1 实例变量和局部变量
实例变量(声明在类中,而不是方法中):
public class Duck{
int size;
}
局部变量(离开作用域,生命消失):
public void foo(int x ){
int i = x + 2;
boolean b = true;
}
实例变量有默认值,局部变量没有默认值。
2.2 堆、栈
方法和局部变量都是存放在栈,所有对象存放在堆。
栈:
- 所有局部变量
- 对象引用变量(指针)与primitive主数据类型变量
堆:
2.2.1 CG
一旦对象的引用生命结束,那它所指的对象就会从堆中被踢出。
三种释放对象的方法:
void go(){
Life f = new Life();
}
Life f = new Life();
z = new Life();
Life f = new Life();
f = null;
2.3 构造函数
只有当没有手动创建构造函数,编译器才会帮忙创建默无参构造。
2.4 构造执行顺序
先执行父类的构造,再执行子类的构造。
class Animal{
public Animal(String name){
System.out.println("Making " + name);
}
public void eat(){
System.out.println("Animal");
}
}
class Cat extends Animal{
public Cat(String name){
super(name);
System.out.println("Making Cat");
}
}
public class Test {
public static void main(String[] args){
Cat c = new Cat("miaomiao");
}
}
注意??:super 也可以换成this 。
3 继承(extend)
3.1 原则
继承设计原则:当行为程序(程序代码)被多个基本类型共享时,并且二者的关系是IS-A 的关系(不是HAS-A 的关系),可以考虑继承(子类是父类的特殊类型)。
注意??:不要为了复用其他类的代码,而强行使用继承。例如河马和钢琴都会发声,从而让河马和钢琴都强行继承一个发声类(这完全不合理)。
3.2 设计方法
继承设计的方法:
- 1,找出具有共同行为和属性的对象;
- 2,设计代表共同状态和行为的类;
- 3,决定子类是否需要让某行为(方法的实现)具有特定不同的运行方式;
- 4,通过寻找使用共同行为的子类来找出更多抽象化的机会(多层抽象);
- 5,完成类的继承层次。
3.3 意义
继承的意义:
- 1,避免重复代码(更改代码时,在不破坏子类的情况下,只需要更改父类代码就可以);
- 2,定义出共同的协议(子类会继承父类的方法,也就是父类可以约束子类【例如抽象方法】)。
3.4 可以被继承的类
- 1,非公有的类只能被同一个包里的类做子类;
- 2,使用final修饰的类,表示它是继承的末端,不能被继承;
- 3,让类只拥有
private 的构造程序(constructor )。
3.5 覆盖父类的方法
如果想要覆盖父类的方法,就需要自类中覆盖的方法与父类中被覆盖的方法拥有相同的参数、返回类型和更开放的存取权限(方法名也需要相同)。
注意??:区分重载(overload)和覆盖,重载只需要方法名相同,其他都可以不同(但是不能只是返回类型不同,而参数相同),与继承中的覆盖没有关系。
3.6 不能被实例化的类(抽象类)
类前使用修饰符abstract 。
抽象基类只有被继承的作用,抽象的class可以有static成员。
不能在非抽象类中拥有抽象方法。抽象类中可以拥有抽象和非抽象方法。
public abstract class Animal{
public abstract void eat();
}
3.7 在父方法中添加内容
class Animal{
public void eat(){
}
}
class Cat extend Animal{
public void eat(){
super.eat();
}
}
3.8 多态
Animal[] animals = new Animal[3];
animals[0] = new Dog();
animals[1] = new Cat();
animals[2] = new Tortoise();
for(animal: animals){
animal.eat();
}
class Vet{
public void gveShot(Animal a){
a.mkaeNoise();
}
}
class PetOwn{
public void start(){
Vet v = new Vet();
Dog d = new Dog();
Cat c = new Cat();
v.giveShot(d);
v.giveShot(c);
}
}
3.9 终极对象(object)
所有类都继承自class object 。
class oject 的一部分方法,boolean() 、equals() 、Class getClass() 、int hashCode() 、String toString() 。建议自己写的类覆盖int hashCode() 、String toString() 、equals() 这几个方法。
没有继承过其他类的类是隐含的继承对象。
作用:
- 1,作为多态可以让方法应付多种类型的机制;
- 2,提供Java执行期对任何对象都有需要的方法的实现的代码(让所有类都会继承到);
- 3,作为一个通用的、轻量化的对象来实现线程的同步化。
任何从ArrayList<Object> 取出的东西都会被当作Object类型的引用,不管它原来是什么。
编译器会根据引用类来判断哪些method可以调用,而不是根据Object确定的类型。
ArrayList<Oject> aList = new ArrayList<Object>();
Cat c = new Cat();
aList.ddd(c);
Object o = aList.get(0);
o.eat();
3.10 接口(interface)
为了解决多重继承的问题(钻石继承),而出现的100%抽象类(类和方法都是abstract )【这样编译器就知道使用的哪个类中的方法】。
//接口
public interface Pet {
public abstract void beFriendly();
public abstract void play();
}
//基类
class Animal{
public void eat(){
System.out.println("Animal");
}
}
//子类
class Cat extends Animal implements Pet{
@Override
public void play() {
}
@Override
public void beFriendly() {
}
}
4 静态
4.1 静态方法
静态方法:以类的名称调用;非静态方法:以变量的名称调用。
Math.min(22, 89);
Song s = new Song();
s.play();
取得新对象的方法:
- 1,new;
- 2,序列化(deserialization);
- 3,反射(Reflection API)。
静态方法不可以调用类的实例变量和静态方法(无论其中是否包含实例变量)。
public class Test{
int size;
public void doNone(){
}
public static void main(String[] args){
System.out.println(size);
}
}
4.2 静态变量
概念:它的值对于所有的实例来说都相同,被同类的所有实例共享的变量。
特点:
- 静态变量会在该类的任何静态方法执行之前完成初始化;
- 如果静态变量没有被赋予初值,它会被赋予默认初值。
注意??:
public class Test{
int size;
static int count;
public static void main(String[] args){
System.out.println(count);
}
}
4.3 静态final变量
含义:创建时必须被初始化,一旦被初始化后,就不会改动。
public static final double PI_NUM = 3.1415926535
??:
final扩展:
- final的变量:不能改变值;
- final的method:不能覆盖该方法;
- final的类:不能继承该类(也就是创建它的子类)。
Java自动导入的包,java.lang 。
4.4 静态import
import java.lang.Math;
public class Test{
public static void main(String[] args){
System.out.println(Math.sqrt(22));
}
}
使用static import:
import static java.lang.Math.*;
import static java.lang.System.out;
public class Test{
public static void main(String[] args){
out.println(sqrt(22));
}
}
4.5 静态代码块
静态代码块指 Java 类中的 static{ } 代码块,主要用于初始化类,为类的静态变量赋初始值,提升程序性能。
静态代码块的特点如下:
- 静态代码块类似于一个方法,但它不可以存在于任何方法体中。
- 静态代码块可以置于类中的任何地方,类中可以有多个静态初始化块。
- Java 虚拟机在加载类时执行静态代码块,所以很多时候会将一些只需要进行一次的初始化操作都放在 static 代码块中进行。
- 如果类中包含多个静态代码块,则 Java 虚拟机将按它们在类中出现的顺序依次执行它们,每个静态代码块只会被执行一次。
- 静态代码块与静态方法一样,不能直接访问类的实例变量和实例方法,而需要通过类的实例对象来访问。
public class StaticCode {
public static int count = 0;
{
count++;
System.out.println("非静态代码块 count=" + count);
}
static {
count++;
System.out.println("静态代码块1 count=" + count);
}
static {
count++;
System.out.println("静态代码块2 count=" + count);
}
public static void main(String[] args) {
System.out.println("*************** StaticCode1 执行 ***************");
StaticCode sct1 = new StaticCode();
System.out.println("*************** StaticCode2 执行 ***************");
StaticCode sct2 = new StaticCode();
}
}
5 异常处理
如果如下的格式,处理异常:
try {
}catch (Exception ex){
}finally{
}
如果要处理多重异常,异常的大小关系需要从小到大(继承树中从下到上),不然可能导致后面需要专门处理的异常种类被前面的代码捕获。
如果有方法可能会抛出异常,就要按如下的方式声明异常:
public void mothed() throw BadException{
if(condition){
throw new BadException;
}
}
如果不想处理异常,可以使用如下的方法(把异常抛给栈上方的方法):
public void method() throw BadException{
laundry.doLaundry;
}
|