Java QuickStart (1)

Java QuickStart 系列是个人学习Java的入坑笔记,求老司机带路。

基础

数据类型

基本数据类型:数值型(byte、short、int、long、float、double),字符型(char),布尔型(boolean)
引用数据类型:类,接口,数组

自动转换

也叫扩大转换

  1. 转换前后数据类型兼容
  2. 转换后的数据类型表示范围比转换前大 如,short类型转换为 int 类型

任何数据类型碰到 String 类型,都会向 String 类型转换

强制转换

也叫显式转换,可能会丢失精度

float f = 33.3f
int x = (int) f;

null

null 表示引用数据类型的默认值

面向对象

Java 内存区域

  1. 栈内存空间,保存所有对象名称(保存了引用堆内存空间地址)
  2. 堆内存空间:保存每个对象的具体属性内容
  3. 全局数据区:保存 static 类型的属性
  4. 全局代码区:保存所有方法定义

this 关键字

使用 this 调用本类中的属性:this.name = ""
this() 可以用来调用构造方法
this 表示当前实例化的对象

static 关键词

使用 static 表示类属性
static 方法不可以调用非 static 类型的属性和方法
可以在 Class 中声明 static { 静态代码块 },它会优于主方法执行

内部类

在类内部可以通过 public 或 private 声明内部类,访问权限与变量和方法完全相同。
在类中定义内部静态类
在方法中定义内部类

super()

子类默认会自动调用父类的无参构造。super()。
子类可以手动调用父类的有参构造 super(参数)

final

使用 final 声明的类不能有子类
使用 final 声明的方法不能被子类覆盖
使用 final 声明的变量即成为常量,常量不可以修改。

抽象类

包含一个抽象方法的类必须是抽象类,都要使用 abstract 关键字声明
抽象方法只需要声明,不需要实现。抽象方法不能使用 private 声明
public abstract void print();

接口

接口可以理解为一种特殊的类,是由全局常量和公共的抽象方法组成。并且默认是这样,所以声明时候可以简化。
接口的抽象方法必须定义 public 权限

克隆

实现 Cloneable 接口,并覆写 clone() 方法

class Person implements Cloneable {
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

多态性

多态主要体现两方面:
1. 方法的重载与重写
2. 对象的多态性

对象的多态性,分为向上转型,向下转型。

向上转型,程序会自动完成。
向下转型,必须明确指明要转型的子类类型。
向上转型关系发生以后,调用的方法是子类覆盖后的方法。并且无法调用子类独有的方法。

instance of 关键词,用于判断一个对象是否是一个类的实例

返回 boolean

抽象类和接口的区别

区别点 抽象类 接口
定义 包含一个抽象方法的类 抽象方法和全局常量的集合
组成 构造方法、抽象方法、普通方法、常量、变量 常量、抽象方法
使用 子类集成抽象类 子类实现接口
关系 抽象类可以实现多个接口 接口不能继承抽象类,但可继承多个接口
设计模式 模板设计 工厂设计、代理设计
对象 通过对象的多态产生实例化对象
局限 抽象类有单继承的局限 接口没有此局限
实际 作为模板 作为标准或一种能力
选择 如果两者都可以用,优先使用接口,避免单继承局限
特殊 一个抽象类可以包含多个接口,一个接口可以包含多个抽象类

类的设计中,尽量不要去继承一个已经实现好的类,而通过继承抽象类或实现接口来实现,如果两者都可以使用,那么优先使用接口。

Object 类

所有的类都有一个公共的父类,Object。有以下主要方法: equals()、hashCode()、toString();
Object 类提供的 equals() 方法默认是比较地址的
比如 String 类就继承了Object类,单独覆盖实现了 equals() 方法

既然Object是所有对象的父类,则所有的对象都可以向 Object 进行转换(向上转型),即一切引用数据类型都可以使用 Object 进行接收

包装类

Java设计中提倡一切皆对象。
所以需要将基本数据类型进行包装:

int -> Integer
char -> Character
short -> Short
long -> Long
float -> Float
double -> Double
boolean -> Boolean
byte -> Byte

其中数值类型包装类,主要又继承自 Number 类。它提供一些拆箱方法。
基本数据类型与包装类的互转,称为装箱与拆箱操作。

int x = 30;
Integer i = new Integer(x); // 装箱
int temp = i.intValue(); // 拆箱

包装类运用最多的,还是进行字符串数值转换,如:

string str1 = "30";
string str2 = "30.3";
int x = Integer.parseInt(str1);
float f = Float.parseFloat(str2);

异常

如果不对异常进行正确的处理,则可能导致程序的中断执行。

如果没有异常处理,就必须要用大量的判断语句。

定义方法时 throws 关键字表示此方法不处理异常,交给方法调用者进行处理
throw 表示抛出异常

可以直接使用 Exception 捕获所有异常,但是捕获范围小的异常必须放在捕获大的异常之前。

e.printStackTrace();

Throwable
- Exception 表示程序中出现的问题,可以用 try...catch处理
- Error JVM 错误,程序无法处理

import * 不会产生性能问题。
如果在不同的包中,有类重名,那么调用的时候需要写出完整的包、类名称。

常用的系统开发包:
java.beans
java.math
java.lang 基本包,目前JDK已经自动导入
java.lang.reflect 反射机制
java.util 工具包,包含常用的类库,重点掌握
java.text 提供了文本处理的类库
java.sql 数据库操作
java.net 网络编程,http、socket、cookie、ip
java.io 输入输出
java.nio
java.security 安全相关,加密算法等
javax.imageio 图形相关
javax.sound 声音相关
javax.xml XML相关

import static 可以静态导入全部使用static声明的静态方法的类。这时候比较像某些语言的函数,因为可以直接使用静态方法调用。

权限控制

private 仅限本类访问
default 可以被本包中的其他类访问
protected 可以被本包及不同包的子类访问
public 所有

应用

多线程

Java 每次运行至少启动2个线程,每当使用Java命令执行一个类时,实际会启动一个JVM,每个JVM就是一个进程,同时启动两个线程:main线程,gc线程。

多线程是指在一个进程执行过程中可以产生多个线程。这些线程可以同时存在,同时运行。

继承Thread类,覆写run方法,通过start执行:

class MyThread extends Thread {
    public void run() {
    
    }
}
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
mt1.start();
mt2.start();

实现 Runnable 接口,实现run方法:

class MyThread implements Runnable {
    public void run() {
    
    }
}
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
Thread t1 = new Thread(mt1);
Thread t2 = new Thread(mt2);
t1.start();
t2.start();

建议使用 Runnable 接口实现多线程,原因:
1. 继承 Thread 类,不能实现资源共享。
2. 避免 Java 单继承特性带来的局限
3. 增强程序健壮性

中断线程 interrupt()
休眠线程 sleep()
后台运行线程 setDaemon()
暂停线程 yield()

同步与死锁

同步:多个操作在同一时间只能有一个线程进行,其他线程要等待此线程完成之后才可以继续运行。

同步代码块
synchronized (this) {
}
同步方法
public synchronized run() {
}

死锁:两个线程都在彼此等待,过多的同步就有可能产生死锁。

死锁的解决办法:
1. 避免过多的同步
2. 确保加锁的顺序一致
3. 提供加锁的超时时间

IO

java.io包中的流主要有字节流、字符流两大类。字节流对应OutputStream/InputStream,字符流对应Writer/Reader类,它们均是抽象类。

字节流操作时不会用到缓冲区,是文件本身直接操作,而字符流使用了缓冲区。比如字符流如果不进行flush()和close()操作,数据会丢失。

字节字符流的转换,是通过OutputStreamWriter/InputStreamReader类来实现的。而Writer类是OutputStreamWriter子类。所以不管是字符流还是字节流,最终都是以字节的形式输入输出的。

管道流,主要作用是可以进行两个线程间的通信。

打印流,是输出信息最方便的类。