java入门知识点查漏补缺
文章目录
1.特点
跨平台/可移植性,安全性,面向对象,高性能,分布式,多线程,健壮性
2.java程序运行过程
JDK:开发>JRE:运行(游戏)>JVM:规范
3.配置环境变量
JAVA_HOME:jdk安装路径
Path:%JAVA_HOME%\bin;
classpath:jdk1.5以上不用配置
检验:cmd->java -version
编译:javac HelloWorld.java
运行:java HelloWorld
注:一个源文件至多只能声明一个public的类,其他类的个数不限
4.注释
//单行注释
/*多行注释
123
123
*/
/**
文档注释,后期生成项目api文档
*/
5.标识符
- 字母,下划线_,美元符号$开头
- 其他部分可以是字母,下划线_,美元符$和数字的任意组合
- 大小写敏感,长度无限制
- 不可以是关键字
命名规范:
- 类名:首字母大写
- 方法或变量名:第一个小写,从第二个单词开始首字母大写(驼峰原则)
- 采用Unicode国际字符集,支持汉字(不推荐)
6.变量
类型 | 声明位置 | 从属于 | 生命周期 |
---|---|---|---|
局部变量 | 方法或语句块中 | 方法/语句块 | 从声明位置开始直到方法结束 |
成员变量(实例变量) | 类内部,方法外部 | 对象 | 对象创建,成员变量也随之创建 |
静态变量(类变量) | 类内部,static修饰 | 类 | 类被加载,静态变量有效 |
//局部变量
public void test(){
int i;
int j=i+5;//编译出错,i未被初始化
}
//成员变量
public class Test{
int i;//如果不自行初始化,它会自动初始化成该类型的默认值
}
//静态变量,如果不自行初始化,与成员变量相同会自动初始化成该类型的默认初始值。
实例变量的默认初始值
int | 0 |
---|---|
double | 0.0 |
char | ‘\u0000’ |
boolean | false |
7.常量
final type var = value;
不能再做修改的量。
命名规则:大写字母和下划线,例如:MAX_VALUE
8.数据类型
-
基本数据类型(三类八种)
-
数值型 byte,short,int,long,double,float
-
字符型 char
-
布尔型 boolean
byte 1字节 -128~127 short 2字节 -32768~32767 int 4字节 大约21亿 long 8字节 264
-
-
引用数据类型:统一为4个字节
- 类 class
- 接口 interface
- 数组
-
java整形默认为int型,声明为long型后加"l"或"L"(不是必须)
-
浮点型 Floating Point Number
- float 单精度 7位有效数字 有一个后缀f或F9(必须加)
- double 双精度 15位有效数字 默认
float f = 3.14F; double f1 = 3.14; double f2 = 3.14D; //错误: float f3 = 3.14;//从double转为float可能会有精度损失,不能直接赋值给float //3.14是double型的浮点常量,不能直接赋值给float
- 浮点型不适合在容许0误差的金融计算领域,如果需要进行不产生误差的精确计算需要用BigDecimal类
- 浮点型数据比较一:
float f = 0.1f; double d = 1.0/10; System.out.println(f==d) //结果为:false // == 在基本数据类型中用于比较数值;在引用数据类型中比较地址值
- 浮点型数据比较二:
float f = 4231315534213L; float f1 = d1+1; System.out.println(f == f1); //结果为:true
- 浮点数是不精确的,尽量不要直接比较,可能会有问题。
- 字长有限,浮点数能够精确表示的数是有限的,因而也是离散的,浮点数一般存在误差,很多数字无法精确的表示,结果只是接近,不是等于。
- java.math包下两个有用的类:BigInteger和BigDecimal类,可以处理任意长度的数值。BigInteger实现了任意精度的整数运算。BIgDecimal实现了任意精度的浮点运算。
- 雷区!:
- 不要使用浮点数进行比较!!!
- 需要比较时使用BigDecimal类
-
字符型 char
- Unicode编码,65536个字符
- 'A’是一个字符,"A"是含有一个字符的字符串
- 占用2个字节
转义字符 含义 unicode编码 \b 退格 \u0008 \n 换行 \u000a \r 回车 \u000d \t 制表符(tab) \u0009 \" 双引号 \u0022 \’ 单引号 \u0027 \\ 反斜杠 \u005c - String类其实就是字符序列
-
布尔型 boolean
- 两个常量值:true和false
- 内存中占1个字节或4个字节
- 注意:不可以使用0或非0的整数替代true和false,这点和C语言不同。
- if(flag==true)不推荐使用
- 建议:if(flag)或if(!flag)
- 关于boolean类型几个字节的说明:
- 在《java虚拟机规范》书中描述:虽定义了boolean这种数据类型,但又对它提供有限支持,在java虚拟机中没有任何供boolean值专用的字节码指令。java语言表达式所操作的boolean值在编译后都使用java虚拟机中的int数据类型来代替,而boolean数组将会被编译成java虚拟机中的byte数组,每个boolean元素占8位,也就是说,JVM规范指出boolean当做int处理也就是4个字节,boolean数组当做byte数组处理,占1个字节。
- 得出boolean类型单独使用是4个字节,在数组中则是1个字节。
9.运算符 (operator)
-
算术运算符
- 整数运算:两操作数一个为long,结果为long;没有long时,结果为int,即使操作数为short,byte,结果也是int
- 浮点运算:两操作数一个为double,结果为double;只有两个操作数都是float,则结果才是float
- 取模运算:操作数可以为浮点数,一般使用整数,结果是“余数”;“余数”符号和左边操作数相同,如:7%3=1,-7%3=-1,7%-3=1
-
赋值运算:
- a*=b+3; //等价于:a = a*(b+3)
-
逻辑运算:结果都是boolean值
逻辑运算 符号 含义 逻辑与 &(与) 两个操作数为true,结果为true,否则为false 逻辑或 |(或) 有一个为true,结果为true 短路与 && 只要有一个为false,直接返回false 短路或 || 只要有一个true,则返回true 逻辑非 ! 取反:!false为true,!true为false 逻辑异或 ^ 相同为false,不同为true -
位运算:二进制的运算
符号 含义 ~ 取反 & 按位与 | 按位或 ^ 按位异或 << 左移,左移一位相当于乘以2 >> 右移,右移一位相当于除以2 - 3*2怎样算最快?
- 左移一位 3<<1
- 3*2怎样算最快?
-
优先级的问题
- 算术 > 关系(> >= < <= == !=)> 逻辑(&>^>|)> && > ||
- 注意:逻辑与,逻辑或,逻辑异或的优先级
- 逻辑非>逻辑与>逻辑或
// a||b&&c的运算结果为:a||(b&&c),而不是(a||b)&&c
10.自动类型转换
- 容量小的数据类型可以自动转换为容量大的数据类型
-
可以将整型直接赋值给byte,short,char等类型变量,而不需要进行强制类型转换,只要不超出范围即可。
short b = 12;//合法 short b1 = 1234567;//非法,超出范围
11.强制类型转换(cast)
-
强制类型转换,又被称为造型(cast),用于强制的转换一个数值的类型。在有可能丢失信息的情况下进行的转换是通过造型来完成的,但可能造成精度降低或溢出。
-
当将一种类型强制转换成另一种类型,而又超出了目标类型的表数范围,就会被截断成为一个完全不同的值。
int x = 300; byte bx = (byte)x; //值为 44
-
不能在布尔类型和任何数值类型之间做强制类型转换
12.随机数
-
Math.random()用于产生0到1区间的double型的随机数,但不包括1
int i = (int)(Math.random()*6);//产生[0,5]之间的随机数
13.控制语句
- switch语句:根据表达式的值从相匹配的case开始执行,一直执行到break或switch末尾,如果不匹配,则进入default语句
- switch语句的case标签:在jdk1.5之前必须是整数(long类型除外)或枚举,不能是字符串,在jdk1.7之后允许使用字符串(String)
14.方法的重载(overload)
- 方法名相同,参数不同的方法
- 不同的含义:形参类型,形参个数,形参顺序不同
- 只有返回值不同不构成方法的重载
- 如:int fun(String str)与void fun(String str)不构成方法重载
- 只有形参的名称不同,不构成方法的重载
- 如:int a(String str){}与 int a(String s){}不构成方法重载
15.面向对象与面向过程的理解
- 表结构对应:类结构
- 一行数据对应:一个对象
- 表中所有数据对应:这个类的所有对象
- 面向过程:执行者思维,解决简单问题(如何开车?)
- 面向对象:设计者思维,解决复杂、需要协调的问题(车由什么组成?)
- 两者相辅相成,宏观上:通过面向对象整体设计;微观上:执行和处理数据仍是面向过程
16.构造方法(构造器 constructor)
this.id(表示属性id) = id(表示形参id);
- java通过new关键字来调用构造器,从而返回该类的实例
- 构造器有返回值!但不能定义返回值类型(返回值类型肯定是本类)不能使用return
- 如果没有定义,则编译器会自动定义一个无参的构造器,如果已定义,则不会自动添加!
- 构造器方法名必须和类名一致!!
- 对象的创建完全是由构造方法实现的吗?
- 不完全是。构造方法是创建 Java 对象的重要途径,通过 new 关键字调用构造器时,构造器也确实返回了该类对象,但这个对象并不是完全由构造器负责创建的。创建一个对象分为如下四 步:
- 分配对象空间,并将对象成员变量初始化为 0 或空
- 执行属性值的显式初始化
- 执行构造方法
- 返回对象的地址给相关的变量
- 不完全是。构造方法是创建 Java 对象的重要途径,通过 new 关键字调用构造器时,构造器也确实返回了该类对象,但这个对象并不是完全由构造器负责创建的。创建一个对象分为如下四 步:
17.JVM内存模型
-
从属于线程的内存区域(栈、计数器)
JVM 的内存划分中,有部分区域是线程私有的,有部分是属于整个 JVM 进程;我们将这部分归为一类。
1. 程序计数器(Program Counter Register),在 JVM 规范中,每个线程都有自己的 程序计数器。这是一块比较小的内存空间,存储当前线程正在执行的 Java 方法的 JVM 指令址,即字节码的行号。如果正在执行 Native 方法,则这个计数器为空。 2. Java 虚拟机栈(Java Virtal Machine Stack),同样也是属于线程私有区域,每个线程在创建的时候都会创建一个虚拟机栈,生命周期与线程一致,线程退出时,线程的虚拟机栈也回收。虚拟机栈内部保持一个个的栈帧,每次方法调用都会进行压栈,JVM 对栈帧的操作只有出栈和压栈两种,方法调用结束时会进行出栈操作。该区域存储着局变量表,编译时期可知的各种基本类型数据、对象引用、方法出口等信息。 3. 本地方法栈(Native Method Stack)与虚拟机栈类似,本地方法栈是在调用本地方法时使用的栈,每个线程都有一个本地方法栈。
-
堆(heap)
堆(Heap),几乎所有创建的 Java 对象实例,都是被直接分配到堆上的。堆被所有的线程所共享,在堆上的区域,会被垃圾回收器做进一步划分,例如新生代、老年代的划分。Java 虚拟机在启动的时候,可以使用“Xmx”之类的参数指定堆区域的大小。
-
方法区(Method Area)
方法区与堆一样,也是所有的线程所共享,存储被虚拟机加载的元(Meta)数据,包括类信息、常量、静态变量、即时编译器编译后的代码等数据。
方法区是一种 java 虚拟机的规范。由于方法区存储的数据和堆中存储的数据一致,实质上也是堆,因此,在不同的 JDK 版本中方法区的实现方式不一样。 JDK7 以前,方法区就是堆中的“永久代”。
JDK7 开始去“永久代”,把静态变量、字符串常量池都挪到了堆内存中。 JDK8 以后,“永久代”不存在了。存储的类信息、编译后的代码数据等已经移动到了MetaSpace(元空间)中,元空间并没有处于堆内存上,而是(直接内存)直接占用的本地内存(NativeMemory)。
-
运行时常量池(Run-Time Constant Pool)(方法区中)
这是方法区的一部分。常量池主要存放两大类常量:
- 字面量(Literal),如文本字符串、final 常量值。
- 符号引用,存放了与编译相关的一些常量,因为 Java 不像 C++那样有连接的过程,因此字段方法这些符号引用在运行期就需要进行转换,以便得到真正的内存入口地址。
-
直接内存(Direct Memory)
直接内存并不属于 Java 规范规定的属于 Java 虚拟机运行时数据区的一部分。Java 的NIO 可以使用 Native 方法直接在 java 堆外分配内存,使用 DirectByteBuffer 对象作为这个堆外内存的引用。
18.程序执行的内存分析过程
Java 虚拟机的内存可以简单的分为三个区域:虚拟机栈 stack、堆 heap、方法区method area。
- 虚拟机栈(简称:栈)的特点如下:
- 栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
- JVM 为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
- 栈属于线程私有,不能实现线程间的共享!
- 栈的存储特性是“先进后出,后进先出”
- 栈是由系统自动分配,速度快!栈是一个连续的内存空间!
- 堆的特点如下:
- 堆用于存储创建好的对象和数组(数组也是对象)
- JVM 只有一个堆,被所有线程共享
- 堆是一个不连续的内存空间,分配灵活,速度慢!
- 方法区(又叫静态区,也是堆)特点如下:
- 方法区是 JAVA 虚拟机规范,可以有不同的实现。
- JDK7 以前是“永久代”
- JDK7 部分去除“永久代”,静态变量、字符串常量池都挪到了堆内存中
- JDK8 是“元数据空间”和堆结合起来。
- JVM 只有一个方法区,被所有线程共享!
- 方法区实际也是堆,只是用于存储类、常量相关的信息!
- 用来存放程序中永远是不变或唯一的内容。(类信息【Class 对象,反射机制中会
重点讲授】、静态变量、字符串常量等)
19.垃圾回收机制
-
内存管理
Java 的内存管理很大程度指的就是:堆中对象的管理,其中包括对象空间的分配和释
放。
对象空间的分配:使用 new 关键字创建对象即可
对象空间的释放:将对象赋值 null 即可。垃圾回收器将负责回收所有”不可达”对象
的内存空间。 -
垃圾回收过程
- 发现无用的对象
- 回收无用对象占用的内存空间。
-
垃圾回收相关算法
- 引用计数法
- 根搜索法(引用可达法)
-
JVM将堆内存划分为:Eden,Survior,Tenured空间
-
三种垃圾回收器:Minor GC:用于清理年轻代区域;Major GC:用于清理年老代区域;Full GC:用于清理年轻代、年老代区域,成本较高,会对系统性能产生影响。
-
在对 JVM 调优的过程中,很大一部分工作就是对于 Full GC 的调节。有如下原因可能
导致 Full GC:- 年老代(Tenured)被写满
- 持久代(Perm)被写满
- System.gc()被显式调用(System.gc是通知JVM,并不是运行)
- 上一次 GC 之后 Heap 的各域分配策略动态变化
-
开发中容易造成内存泄露的操作:
- 创建大量无用对象
- 静态集合类的使用
- 各种连接对象(IO 流对象、数据库连接对象、网络连接对象)未关闭
- 监听器的使用不当
-
其他要点:
- 程序员无权调用垃圾回收器。
- 程序员可以调用 System.gc(),该方法只是通知 JVM,并不是运行垃圾回收器。尽量少用,会申请启动 Full GC,成本高,影响系统性能。
- finalize 方法,是 Java 提供给程序员用来释放对象或资源的方法,但是尽量少用
20. this、static、super关键字
-
this:创建好的对象的地址
- 用于程序中产生二义性之处;普通方法中,this总是指向调用该方法的对象;构造方法中,this总是指向正要初始化的对象
- 调用重载的构造器,必须位于构造方法的第一句。
- this不能用于static方法中。
-
static关键字:
- 该类的公用变量,属于类,类被载入时显式初始化
- static成员变量只有一份,被该类的所有对象共享
- 一般用”类名,类属性/方法“来调用
- 在 static 方法中不可直接访问非 static 的成员。
-
静态初始化块
- 构造方法用于对象的初始化!静态初始化块,用于类的初始化操作!
- 在静态初始化块中 不能直接访问非 static 成员。
-
super关键字
- 直接父类对象的引用
- 可以通过 super 来访问父类中被子类覆盖的方法或属性
21.包机制
- com.java和com.java.haha这两个包没有包含关系,是两个完全独立的包,只是逻辑上看起来后者是前者的一部分。
- java会默认导入java.lang包下的所有类,因此这些类可以直接使用
- import java.util.*; 导入该包下的所有类,会降低编译速度,但不会降低运行速度。
- 如果导入两个同名的类,只能用包名+类名来显示调用相关类:
java.util.Date date = new java.util.Date();
22.继承和组合
- java类没有多继承、接口有多继承
- 子类继承父类,可以得到父类的全部属性和方法(除父类的构造方法),但不见得可以直接访问(比如父类私有的属性和方法)
- 默认父类是java.lang.object
- instanceof 是二元运算符,左边是对象,右边是类;当对象是右面类或子类所创建对
象时,返回 true;否则,返回 false。 - final 关键字的作用:
- 修饰变量: 被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。
- final int MAX_SPEED = 120;
- 修饰方法:该方法不可被子类重写。但是可以被重载!
- final void study(){}
- 修饰类: 修饰的类不能被继承。比如:Math、String 等。
- final class A {}
- 修饰变量: 被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。
- 组合:将父类对象作为子类的属性:子类通过调用这个属性来获得父类的属性和方法
- “is -a”关系建议使用继承
- “has-a”关系建议使用组合。
23.封装
-
高内聚、低耦合
-
访问控制符
-
-
private 表示私有,只有自己类能访问
-
default 表示没有修饰符修饰,只有同一个包的类能访问
-
protected 表示可以被同一个包的类以及其他包中的子类访问,不同包中的非子类无法访问
-
public 表示可以被该项目的所有包中的所有类访问——只有继承子类能用
-
【注】关于protected的两个细节:
- 若父类和子类在同一个包中, 子类可访问父类的 protected 成员,也可访问父类对象的 protected 成员。
- 若子类和父类不在同一个包中,子类可访问父类的 protected 成员,不能访问父类对象的 protected 成员。
24.多态(polymorphism)
-
同一个方法调用,由于对象不同可能会有不同的行为
-
要点:
-
多态是方法的多态,不是属性的多态(多态与属性无关)
-
多态的存在有3个必要条件:继承,方法重写,父类引用指向子类对象
-
父类引用指向子类对象后,用父类引用调用子类重写的方法
-
编译看左边,运行看右边
-
public static void main(String[ ] args) { Animal a1 = new Cat(); // 向上可以自动转型 //传的具体是哪一个类就调用哪一个类的方法。大大提高了程序的可扩展性。 animalCry(a1); Animal a2 = new Dog(); animalCry(a2); //a2 为编译类型,Dog 对象才是运行时类型。 /*编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换。 * 否则通不过编译器的检查。*/ Dog dog = (Dog)a2;//向下需要强制类型转换 dog.seeDoor(); }
-
向下转型中使用instanceof关键字
public class TestCasting3 { public static void main(String[ ] args){ Object obj = new String("哈哈哈"); if(obj instanceof String){ String str = (String)obj; System.out.println(str.charAt(0)); }else if(obj instanceof StringBuffer){ StringBuffer str = (StringBuffer) obj; System.out.println(str.charAt(0)); } } }
-
25.抽象类
- 有抽象方法的类只能定义为抽象类
- 抽象类不能实例化,即不能用new来实例化抽象类
- 抽象类可以包含属性,方法,构造方法,但是构造方法不能用来New实例只能用来被子类调用
- 抽象类可以有抽象方法,也可以有普通方法
- 抽象类只能用来被继承
- 抽象方法必须被子类实现
26.接口
- 比“抽象类”还“抽象”的“抽象类”
- 访问修饰符只能是public或默认(类同样如此)
- 接口名:和类名相同的命名机制
- extends:接口可以多继承
- 常量:接口中的属性只能是常量,总是public static final修饰
- 方法:接口中的方法只能是public abstract,可省略
- 要点:
- 子类通过implements来实现接口中的规范
- 接口不能创建实例,但是可用于声明引用变量类型
- 一个类实现了接口,必须实现接口中的所有方法,并且这些方法只能是public的
- JDK1.8之前接口中只能包含静态常量,抽象方法,不能有普通属性,构造方法,普通方法
- JDK1.8之和接口中包含普通的静态方法,默认方法
- 默认方法:允许给接口添加一个非抽象的方法实现,只需用default即可,所有这个接口的实现类都会通过继承得到这个方法
- 静态方法从属于接口,可以通过接口名直接调用
27.内部类
java内部类有四种,分别是静态内部类、非静态成员内部类、局部内部类、匿名内部类。
-
成员内部类(静态/非静态)
-
静态:
-
public class Person { private String name ="小明"; //静态成员内部类 static class Man{ private String name; private int age; public void show(){ System.out.println("男士"); } } } 测试: //创建静态的Man内部类的实例(静态的成员内部类) Person.Man man = new Person.Man(); man.show();
-
非静态:
-
public class Person { private String name ="小明"; //非静态成员内部类 class Woman{ private String name="杜鹃"; public Woman(){ } public void display(String name){ System.out.println(name);//方法的形参 System.out.println(this.name);//内部类的属性 System.out.println(Person.this.name);//外部类的属性 } } } 测试: //创建非静态的Woman内部类的实例(非静态的成员内部类) Person p = new Person(); Person.Woman woman = p.new Woman(); woman.display("lucy");
-
-
局部内部类
-
public class Person { private String name ="小明"; //局部内部类使用 public Comparable getComparable(){ //创建一个实现了Comparable接口的类: //方式一:局部内部类 class MyComparable implements Comparable{ @Override public int compareTo(Object o) { return 0; } } return new MyComparable(); } }
-
-
匿名内部类
-
适合只需使用一次的类,比如:键盘监听操作等,在安卓开发,awt,swing中常见
-
public class Person { private String name ="小明"; public Comparable getComparable(){ //方式二:匿名内部类的方式 return new Comparable(){ @Override public int compareTo(Object o) { return 0; } }; } }
-