前提概要
- Java的工作方式
源代码=》编译器=》产出字节码( 生成.class文件)=》 Java虚拟机(JVM) - 程序要做的事情
你会编写源代码文件(.java),用javac编译程序把文件进行编译(输出.class文件,这个文件由字节码组成),然后在某个Java虚拟机上执行编译过的字节码(JVM会将字节码转换成平台能够理解的形式来运行)
1、Java的primitive主数据类型有三种
boolean与char,数值 (带正负号)
数值分为六种: byte\short\int\long\float\double
2、如果实例变量定义了,但是没有赋值,默认值,数值类型会是0,Boolean类型是false,对象类型是null
null代表没有操作对象的远程控制,它是个引用而不是对象
3、对象和类的区别
类是对象的蓝图。它会告诉虚拟机如何创建某种类型的对象。根据某类创建出的对象都会有自己的实例变量
举例:
编写类=》编写测试用的类=》在测试用的类中创建对象并存取对象的变量和方法
4、main方法,是程序的入口
由两个作用:一个是测试真正的类,二是启动你的Java应用程序
5、实例变量和局部变量的区别
- 实例变量是声明在类中,而不是方法中
- 局部变量声明在方法中,且没有默认值,如果在变量初始前使用,编译器会显示错误
- 局部变量在使用前,必须初始化
- 方法的参数也是局部变量,但是方法调用时,肯定会初始化,所有编译器不报错
6、变量的比较有“==”和equals()两个方法
- 两等用于比对两个变量的字节组合,用于判断primitive主数据和两个引用变量是否引用到同一个对象
7、for循环有两种
- 基础版:for (int i =0 ; i < 8 ; i++) {}
- 加强版: for(String name:nameArray){}
8、primitive主数据可以进行转换,通过cast运算符
long y=42;
int x=(int)y;
short z = (short)y;
9、数组和ArrayList的区别
- 在使用ArrayList时,你只是在运用ArrayLi st类型的对象,因此就跟运用其他的对象样你会使用
.运符来调用它的方法 - 使用数组时,你会以特殊的数组语法来操作. 这样的语法只能用在数组上. 虽然说数组也是对象,但是它有自己的规则,你无法调用它的方法,最多只能存取它的length实例变量.
- 一般数组在创建时就必须确定大小,而且不能改变;
10、超强布尔表达式
- 短运算符( && ,||),遵循短路原理
- 不等于运算符(!=和!)
- 长运算符( &,|):该运算符使用在Boolean表达式时会强制Java虚拟机一定要计算运算符两边的算式. 但这两个运算符通常是用来作位的运算.
11、使用函数库
- System (Syslem.out.println),String与Math ( Math.random)属于java.lang这个包.
- 你必须指明程序代码中所使用到的类的完整名称,除了
java.lang - javax代表标准版的扩展
- import只是帮你节省每个类前面的包名称而已,不会因为用了import而变大或者变慢
// java.util包名,ArrayList类名
java.util .ArrayList
- 放一个Import述句在程序源文件的最前面
```
import java.util .ArrayList
```
- 第二种方式,在每个使用的地方打出全名
```
java.util .ArrayList<Dog> List = new java.util .ArrayList<Dog>()
```
12、查询API,了解Java有哪些类
Java17中文文档 - API参考文档 - 全栈行动派
13、在 Java 中,IS-A 测试(也称为类型检查)是一种判断对象是否属于某个特定类型(类、接口)的机制。它用于在运行时验证对象的继承关系或接口实现,确保代码安全地处理不同类型的对象。
14、存取权限(access level ) 控制了谁可以接触什么
下而列出这4种权限,左边是最受限制的,而越往右,限制越小
private default protected public
- private类型成员不会被继承
- public类型成员会被继承
15、《Head First 设计模式》
16、继承
- 子类继承父类使用extends
- 如果既继承父类,又覆盖一部分,使用
super调用方法
17、多态 - 在多态下,引用类型和对象可以是不同类型,比如引用是Animal,对象是Dog
Animal dog = new Dog()
- 任何extends过声明引用类型的对象,都可以赋值给这个引用变量
Animal[] animals= new Animal[4]
animals[0] = new Dog()
animals[1] = new Cat()
animals[2] = new Wolf()
animals[3] = new Hippo()
- 参数和返回类型也可以是多态
-
final修饰符:如果用它标识类,表示被修饰的类是继承树的末端,不能被继承;如果标识方法,表示该方法不可以被覆盖
18、覆盖的原则
- 参数必须要一样,且返回类型必须兼容:子类对象得保证能够执行父类的一切
- 不能降低方法的存取权限
19、方法的重载:两个方法的名称相同,但参数不同
注意:重载版的方法只是刚好有相同名字的不同方法,它与多态或继承无关,重载的方法与覆盖的方法不同
- 返回类型可以不同
- 不能只改变返回类型
- 可以更改存取权限
20、抽象类: 可以继承和产生多态, 但是要限制只有它的子类能够被初始化
- 关键字:
abstractclass - 抽象类除了被继承过之外,是没有用途,没有值,没有目的的
21、不是抽象的类,就被称为具体类
22、抽象的方法:此方法一定要被覆盖过
- 关键字:
abstract - 抽象方法没有实体
public abstract void eat();// 没有方法体,直接以分号结尾
- 抽象方法只能在抽象类中定义,如果一个类里面有方法被定义为抽象方法,这个class必须也定义为抽象类
- 好处是实现多态,每一个子类里面,都有这个方法
- 在继承树结构下的第一个具体类必须要实现出所有的抽象方法
23、在Java中的所有类都是从Object这个类继承出来的
- 对象会带有从父类继承下来的所有东西,这代表每个对象,不论实际类型,也会是个Object的实例
- 把Object类型转换成Dog类型
ArrayList<Object> myArrayList = new ArrayList<Object>()
Dog aDog = new Dog()
myArrayList.add(aDog)
Object o = myArrayList.get(0) // o会被当成Object的实例,失去dog的相关属性
// 如果想恢复成dog,可以采用以下方式
Dog d = (Dog)o
// 如果不确定类型,可以加类型判断
if(o instanceof Dog){
Dog d = (Dog)o
}
24、致命方块:一个类继承多个类(多重继承)时,如果存在同名的方法或者变量,无法选定哪个有效
- 解决方法,使用接口
Interface,把全部的方法设置为抽象 - Java的接口就好像是100%的纯抽象类
25、接口的定义和实现
// 接口定义
public interface Pet{}
// 接口实现
public class Dog extends Canie implements Pet{
public abstract void beFriendly();// 接口方法一定是抽象类,所以必须分号结尾,没有内容
public abstract void play();
}
// 类可以实现多个接口
public class Dog extends Canie implements Pet,saveable{}
26、实例变量是被声明在类而不是方法里面,实例变量存在于所属的对象中
- 实例变量存在于对象所属的堆空间上
27、局部变量和方法的参数被声明在方法中,他们的生命是暂时的,仅存在于方法执行阶段,也就是方法调用到执行完毕为止
- 所有的局部变量都存在于栈上相对应的堆栈块中
28、唯一能够调用构造函数的方法就是新建一个类
- 构造函数带有你在初始化对象时会执行的程序代码,也就是新建一个对象时就会被执行
- 构造函数没有返回类型,函数名与类名相同
public Duck(){
// 构造函数代码在此
}
- 构造函数可以让你介入new的过程
// 如下,构造函数代码会在每一个Duck实例中执行
public class Duck(){
public Duck(){
// 构造函数代码在此
}
}
- 大部分人使用构造函数初始化对象的状态
- 一个类可以有两个构造函数,一个有参数,一个没有参数
- 编译器只会在没有构造函数的时候,创建一个没有参数的构造函数;如果已经有一个有参数的构造函数,必须自己手动写一个没有参数的
- 如果类有一个以上的构造函数,则参数一定要不一样
- 在创建新对象时,所有继承下来的构造函数都会执行
29、构造函数链
- 创建子类对象,也会同步构建父类对象,这就形成了构造函数链,而且父类对象的构造函数先执行
- 调用父类构造函数的唯一方法是调用
super() - 如果我们没有调用super(),编译器会帮我们加上
- 如果父类有多个重载的构造函数版本,只会调用没有参数的版本
- 父类的部分必须在子类创建之前就完整的成型(就像必须现有父母后有子女一样),所以supper()的调用必须是构造函数的第一个语句
- 子类型先被调用,但是是最后一个完成
30、从某个构造函数调用重载版的另一个构造函数
- 使用this()来从某个构造函数调用重载版的另一个构造函数
- this()只能用在构造函数中,且必须是第一行语句
- super()和this()不能兼得
31、 对象的生命周期:完全看引用它的“引用”
- 局部变量只会存活在声明该变量的方法中
- 实例变量的寿命与对象相同
32、life和scope的区别
- life: 只要变量的堆栈块,还存在于堆栈上,局部变量就算活着,如果声明它的方法在执行 中,它就能被调用,但是如果声明它的方法不在执行中,它虽然活着,但是它不在当前执行方法的scope内,所以不能被调用
- scope: 局部变量的范围只限于声明它的方法之内。当此方法调用别的方法时,该变量还活着,但是不在目前的范围内,执行其他方法完毕返回时,范围也就跟着回来了。
33、静态方法 static:不需要类实例的方法,比如Math
- 一种不需要实例变量也就不需要对象的行为
- 以类的名字调用静态方法,比如
Math.min(88,68),以引用变量的名称调用非静态方法:Song t2 = new Song() t2.play() - 静态方法不能调用非静态的变量,也不能调用非静态的方法
34、静态变量:它的值对所有的实例来说都相同
- 静态变量:每个类一个;实例变量:每个实例一个
- 如果没有给静态变量赋值,它就会被设置默认值,int-->0,浮点数 --> 0.0,boolean-->false,对象引用-->null
- 静态的final变量是常数,常数变量的名称应该要都是大写
public static final double PI = 3.141592653589793
- 如果类只有静态的反复,可以奖构造函数标记为private,以避免被初始化
35、final - final的变量代表你不能改变它的值
- final的method代表你不能覆盖掉该method
- final的类代表你不能继承该类(也就是创建它的子类)
36、封装类型和数据类型转换
- int => string,可以使用
'' + number - 格式话使用formate()方法,%代表传入的参数
format( "%,d" , 1000000000); // 1,000,000,000
format("I have %.2f bugs to fix", 45.09876) // I have 45.10 bugs to fix
format("I have %,.2f bugs to fix", 45678.09876) // I have 45,678.10 bugs to fix
- 要取得当前日期时间用Date,其余功能可以从Calendar上面找
37、import static 导入类中的静态成员(如静态方法或静态变量),不能用于导入类本身
38、异常处理
- 使用try..catch..finally
- 异常是一种Exception 类型的对象
- 在类的后边定义throws + 错误类型
- 实际抛出异常的情况有两种
- 方法体内显示throw异常
- 方法体内调用了其他可能抛出异常的方法,并且没有用try-catch捕获,而是直接向上抛出,这叫duck异常
// 并没有try/catch块来处理有风险的方法,因此这个方法本身就是又风险的
public void foo() throws ReallyBadException{
// 调用有风险的方法
laundry.doLaundry()
}
- 异常支持多态,可以定义多个异常
39、 序列化(保存对象)
- 核心含义:
是将 Java 对象(包括其状态信息,即成员变量的值)转换为字节序列的过程。这样做的目的是方便对象的存储(如写入文件)或网络传输。与之对应的是反序列化,即把字节序列恢复为原来的 Java 对象。
简单说,序列化就是把 "活生生" 的对象 "冻结" 成字节流,反序列化则是把字节流 "解冻" 回对象,让它能在另一个地方(如另一台电脑、另一个进程)继续使用 - 序列化的主要目的是对象的持久化存储或跨平台 / 跨进程传输,它会保留对象的完整结构和数据,生成的字节流通常包含额外的类元信息(如类名、属性类型等),体积可能比原始数据更大
- 数据压缩的目的是减小数据体积,通过算法(如 ZIP、GZIP)去除冗余信息,不涉及对象结构的转换,压缩后的数据只是原始数据的紧凑形式,不能直接恢复为对象
- 序列化程序会将对象版图上的所有东西存储起来,被对象实例变量所引用的所有对象都会被序列化
- FileOutputStream:把字节写入文件
- ObjectOutputStream: 对象被碾平,把对象转换成可以写入串流的数据
- 如果要让类能够被序列化,就实现Serializable,唯一目的是声明有实现它的类是可以被序列化的
import java.io.* // 必须import
// Serializable 没有方法需要被实现,只是用来告诉Java虚拟机,它可以被序列化
public class Box implements Serializable {
}
- 序列化时全有或者全无的,不存在部分成功的场景
- 如果某些实例变量不能或者不应该被序列化,就把它标记为
transient
40、解序列化(还原对象)
- FileInputStream
- ObjectInputStream
41、通过序列化聚在储对象是lava程序在来回执行闹符储和恢复数馆最简
单的方法
42、将字符串写入文本文件
import java.io.*
class WriteFile {
public static void main (String[] args){
try {
FileWriter writer = new FileWriter("Foo.txt")
wirter.write('Hello World')
writer.close()
} catch (IOException ex ) {
ex.printStackTrace()
}
}
}
42、java.io.file class
- File 这个类,代表磁盘上的文件,不代表文件内容
- 可以创建删除文件,返回文件的路径
- 写入文件时,为了提高磁盘写入效率,可以使用缓冲区
- 读取文件也用这个类
43、序列化时,写入和读取时的类要保持一致
- 通过serialVersionUID实现
- 通过serialver工具来取得版本
44、jar包的部署
- 三种方式:本机,半本机半远程,远程
- 将源代码和类文件分开:编译的时候加修饰符号
-d - JAR:Java ARchive--这种文件是个pkzip格式的文件,它能让你把一组类文件包装起来
45、创建可执行的JAR步骤如下 - 确定所有类文件都在classes下
- 创建manifest.txt文件,描述哪个类带main()方法,此文件放在classes文件夹下
Main-Class: MyApp
- 执行JAR工具创建JAR
jar -cvmf manifest.txt app1.jar *.class
46、把类包进包里
- 最好包名和目录结构相对应
package com.headfirstjava
public class packageExercise{}