Java易遗忘知识点
1. java类基础知识
javac
:将代码(.java)编译成字节码文件(.class),如javac HelloWord.java
java
:对字节码文件进行解释,如java HelloWord
一个Java文件里面可以有多个class(内部类),但是只能有一个public class
类是java中最基本的逻辑单元
严格来说,main
函数不属于类的成员方法,也无法被其他方法/类调用。
String[] args
是main函数的形参,args是变量名可以改变。此参数可以接收外界提供给main函数的参数。
1.2 基本类型和选择结构
整形:
- short:
2
个字节,16
位,表示范围 − 2 − 15 -2^{-15} −2−15~ ( 2 15 − 1 ) (2^{15}-1) (215−1) - int:
4
个字节,32
位,表示范围 − 2 − 31 -2^{-31} −2−31 ~ ( 2 31 − 1 ) (2^{31}-1) (231−1) - long:
8
个字节,64
位,表示范围 − 2 − 63 -2^{-63} −2−63 ~ ( 2 63 − 1 ) (2^{63}-1) (263−1)
浮点数:
- float:
4
个字节,32
位 - double:
8
个字节,64
位 - 两者的表示都是采用IEEE 754标准,都不能表示精确的数字。
在java中if
的判断条件只能是布尔值或布尔表达式,不可以是单个数字啥的,区别于C。
1.3 重载及权限修饰符
函数重载(overload),在同一个类中,方法名可以相同,但是参数类型
,参数个数
必须不同。
tips:在jdk1.5以后,允许重载的方法返回值是协变的
修饰符 | 类内 | 同包 | 不同包子类 | 不同包非子类 |
---|---|---|---|---|
private | √ | × | × | × |
(default) | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
private修饰的变量或方法,只有在同一个类的内部被访问。
default不是修饰符,只是为了方便才叫他的default的,如果不写修饰符,那就认为是default
。其修饰的变量及方法只能在一个包中的类才能使用。
protected修饰的变量或方法可以在类内、同包内及不同包的子类所访问。
public修饰的变量或方法可以在不同包中使用。
private
、default
、protected
只能修饰内部类。public
可以修饰所有的类。
2. 面向对象和类
2.1 对象与基本类型的赋值
-
对象赋值是
reference
赋值(相当于C的指针),而基本类型是直接值拷贝
。 -
类成员变量有初值,函数中的临时变量必须要有初始值。
2.2 构造函数
-
在java中有构造函数,无析构函数,因为java具有 内存自动回收机制,当变量退出其生命周期后,JVM会自动回收所分配的对象的内存。
-
构造函数的名称与类名一致,且没有返回值。
-
当你没有实现构造函数时,Java编辑器会自动帮该类产生一个无参构造函数。
3. 继承、抽象类和接口
继承:子类可以继承其父类,其父父类…所有的属性和方法,但不能直接访问父类的private成员。
关键字:extends
。
-
java采用的是单继承,即每个类只能有一个父类。【注意】如果不写
extends
则每个类默认都继承一个java.lang.Object
。又因为,子类会继承其父类、父父类…所有的属性和方法,所以可以认为每个类都继承了java.lang.Object
。 -
每个子类的构造函数的第一句话都默认调用父类的无参构造函数super(),除非在子类的第一句代码就自己写了
super
语句。也就是说子类的构造函数第一行必须调用父类的构造函数。 -
子类使用父类的属性:
super.属性名
。如果是在子类的静态方法里面,可以直接调用父类的静态属性即可以省略super
。 -
子类使用父类的方法:
super.方法名()
。需要注意的是子类不可以重写父类的静态或私有
方法,但可以调用和重新定义。只有override注释的才是真正的重写,没加的都是伪重写(实际要叫重新定义)
运行结果:
说明在省略@override
的前提下,子类并不会真正的重写父类的私有或者静态
方法 ,只有父类的方法跟属性被public
修饰时,才可以省略@override
。
抽象类:使用abstract
声明的类,包含了任意零个或多个的 属性,完整的方法和抽象方法(abstract定义的)。
关键字:abstract
【注意】
- 只有全部方法都是完整的才可以实例化(new)。
- 包含了抽象方法的一定是抽象类,抽象类不一定包含抽象方法。
- 子类可以继承抽象类,但只有实现了父类的所有抽象方法,才可以变成完整类,不然它还是抽象类。
比如下面AB都是抽象类
public abstract class A{
int a;
// 无参构造函数
public A(){}
public void test(){
a++;
}
// 抽象方法
public abstract int sum(){};
}
public abstract class B{
int b;
public void test(){
b++;
}
}
接口:使用interface
声明,所有方法都是public
修饰的,并且方法体里面都无代码且最后以分号结尾。
接口中可以定义变量,但必须是常量(public staic final
修饰),如果未写修饰符,默认就是public staic final
.
比如
public interface A(){
// public static final int a;
int a; // 两者等价
public int sum();
public void eat();
public void walk();
}
- 在java中,类只能单继承,但可以实现多个接口,且继承类的同时可以实现接口。
- 接口实现接口时,没有实现的方法会叠加。
- 类实现接口时,必须实现接口中所有的方法。
总结:
抽象类和接口的相同点:两者都不能被实例化(new)
抽象类和接口的不同点:
- 抽象类用
abstract
声明,接口使用interface
关键字 - 抽象类可以有部分函数是完整实现的,接口中所有方法都不能是完整实现的。
- 一个类只能继承一个抽象类,但可以实现多个接口。【注意】
extends
要写在implements
前面 - 抽象类有构造函数,接口无构造函数。
- 抽象类可以有main,接口中没有main
- 抽象方法可以有多个权限修饰符,接口的中方法只能是public
3.1 转型、多态
转型
子类可以转为父类,父类不可以转为子类,除非父类本身就是从子类转化而来。
比如Human为父类,Man为子类
// 子向父转型
Man man = new Man();
Human obj1 = (Human) man;
// 父转型子的特殊情况
Human obj2 = new Man();
Man man = (Man)obj2;
多态
子类重新定义了父类的方法(名字、参数一致)的行为叫做重写(overwrite)。
子类向父类转换,并且子类重写了父类的方法,这种行为就叫做多态。我们称重载为编译时多态,重写为运行时多态。
子类方法的优先级高于父类。
作用:
-
以统一的接口来操纵某一类中不同对象的动态行为。
-
Human[] hus = new Human[3]; hus[0] = new Man(); hus[1] = new Man(); hus[2] = new Woman(); for(int i=0;i<Human.length;i++){ // 每个对象都执行eat方法 hus[i].eat(); }
-
-
对象之间的解耦(这种方式也叫契约设计)
-
public staic void haveLunch(Human hu){ // 不同对象进来就执行不同的eat方法 hu.eat(); } public static void main(String[] args){ Human[] hus = new Human[3]; hus[0] = new Man(); hus[1] = new Man(); hus[2] = new Woman(); for(int i=0;i<Human.length;i++){ // 每个对象都执行eat方法 haveLunch(hus[i]); } }
-
4. static、final和常量设计
static
-
static可作用在
变量
、方法
、类
、代码块
。 -
static变量只依赖于类存在,即通过类名即可访问。(当然也可以使用对象名来调用)
-
同一类中的static变量都存储在同一空间(栈)中,所有的对象实例都可以共享该空间
-
public class Potato(){ static int price; String name; public Potato(int price, String name){ this.price = price; this.name = name; } public static void main(String args[]){ Potato p1 = new Potato(20, "爆炒土豆丝"); Potato p2 = new Potato(25, "黄焖土豆丝"); } }
-
-
static方法,也是可以直接通过类名来调用。static方法里面的所有东西都必须是静态的,比如静态方法里面禁止调用非静态方法,不可以使用非静态变量。【注意】非静态方法可以调用静态方法。
-
static修饰代码块。static代码块只会在类第一次加载时被调用,即在程序运行期间,static代码块只会运行一次。【注意】执行顺序static块 > 匿名块 > 构造函数
class StaticBlock(){ // 静态代码块 static { System.out.println("0000"); } // 构造函数 public StaticBlock(){ System.out.println("1111"); } // 匿名代码块 { System.out.println("2222"); } }
创建
StaticBlock
对象时,得到的结果为:0000 2222 1111
单例模型
定义:限定某一类在整个程序运行过程中,内存空间中只能保留一个实例对象。
实现思想:采用static共享对象实例,采用private修饰构造函数来防止外界new操作,向外界提供共享对象。
代码实现:
public class SingleTon{
// 共享同一个对象
static SingleTon obj = new SingleTon();
// 防止外部new
private SingleTon(){
}
// 向外界提供实例对象
public static SingleTon getInstance(){
return obj;
}
public static void main(String args[]){
SingleTon s1 = SingleTon.getInstance();
SingleTon s2 = SingleTon.getInstance();
System.out.println(s1==s2); // true
}
}
final
- final修饰的类不能被继承
- 父类中final修饰的方法不能被子类改写
- final修饰的变量
- 对象:不能修改其指针,但可以修改其内部的值
- 基本类型的变量:不能修改其值。
4.1 常量池
C语言的常量定义:const
java的常量定义:public static final
。常量名建议全大写,以连字符_
相连。
常量池:位于**栈
**空间中。相同的值只存储一份,节省内存,共享访问。
java给字符串、很多基本类型的包装类都建立了常量池。
拥有常量池包装类:(超过范围就被赶出常量池)
- Boolean:true,false
- Byte:-128~127
- Character:0~127
- Short、Integer、Long:-128~127
- 【注意】
Float
跟Double
没有常量池
代码演示:
public class Test(){
public static void main(String args[]){
Boolean b1 = true;
Boolean b2 = true;
System.out.println(b1==b2); // true
Integer i1 = -128;
Integer i2 = -128;
System.out.println(i1==i2); // true
Integer i3 = 128; // 超出范围
Integer i4 = 128;
System.out.println(i3==i4); // false
Float f1 = 0.5;
Float f2 = 0.5;
System.out.println(f1==f2); // false
String s1 = "abcd";
String s2 = "a"+"bc"+"d"; // 都是常量,编译器会自动优化
System.out.println(s1==s2); // true
}
}
字符串跟包装类都有两种创建方式:
- 字面量:此方式创建的变量会放在**
栈
**空间中,如String a = “abc”
、Integer i = 10
- new:此方式创建的变量会放在**
堆
**空间中,如String a = new String("abc")
、Integer i = new Integer(10)
tips:栈内存容量小,读取速度快;堆内存容量大,读取速度慢。
【知识点】
- 基本类型跟包装类之间会自动 装箱拆箱。
- 加法
+
操作会使包装类自动 拆箱。
public class Test(){
public static void main(String args[]){
int i1 = 10; // 存在于栈中常量池
Integer i2 = 10; // 存在于栈中常量池
// 基本类型跟包装类进行比较,包装类自动拆箱
System.out.println(i1 == i2); // true
Integer i3 = new Integer(10); // 存在于堆
// 基本类型跟包装类进行比较,包装类自动拆箱
System.out.println(i1 == i3); // true
// 两个对象进行比较,判断其地址
System.out.println(i2 == i3); // false
Integer i4 = new Integer(5);
Integer i5 = new Integer(5);
// 加法操作会使包装类自动拆箱。i4+i5=10,10为int类型
System.out.println(i1 == (i4+i5)); // true
System.out.println(i2 == (i4+i5)); // true
System.out.println(i3 == (i4+i5)); // true
Integer i6 = i4+i5; // 先拆箱再装箱,最后等价于Integer i6 = 10
System.out.println(i1 == i6); // true
System.out.println(i2 == i6); // true
System.out.println(i3 == i6); // false
}
}
【知识点】
- 字符串有 变量或对象 参与相加时不进行优化
euqals
:同类型同内容才会返回true
==
:两个都是对象(相同类型)时,比较地址;至少有一个是基本类型时,比较值
public class Test(){
public static void main(String args[]){
String s1 = "abcd"; // 位于栈中
String s2 = "abc"; // 位于栈中
String s3 = s2 + "d"; // 位于堆中
// 有变量相加,不进行优化
System.out.println(s1==s3); // false
// euqals是比较字符串内容
System.out.println(s1.equals(s2)); // true
String s4 = new String("abc"); // 位于堆中
System.out.println(s2==s4); // false
String s5 = "abc" + new String("d"); // 位于堆中
System.out.println(s1==s5); // false
System.out.println(s3==s5); // false
}
}
4.2 不可变对象
不可变对象一旦创建就不可更改,其包括:
- 八个基本类别的包装类
- String,BigInteger等
优点:
- 只读,线程安全
- 重复使用,提高性能
缺点:制造垃圾,浪费空间
不可变对象在赋值或传参的时候也是传指针。例如:
如果需要对字符串进行大量的修改操作,那么这种方式会大大降低性能,我们可以使用StringBuffer
或StringBuilder
类的append
方法来进行修改。
- StringBuffer / StringBuilder 的对象都是可变对象。
- StringBuffer 是同步的,线程安全,修改快速
- StringBuilder 不同步,线程不安全,修改更快
他们的方法用法一样只是同不同步的问题:
- append / insert / delete
- substring(int beginIndex, int endIndex):返回字符串的子字符串(左闭右开)
length()
字符串实际大小。capacity()
占用空间大小。如果append的对象很长,超过了capacity
的大小,那么capacity
的大小会乘2加1。如果扩容后的capacity
还不够,此时capacity
直接等于字符串长度。- replace(charStr, replStr) / repalceAll(charStr, replStr) :都会用replStr全部替换charStr。只不过前者的
charStr
是纯字符,而后者的charStr
是正则表达式。
5. Java常用类
包名以Java开头的包是Java的核心包。
包名以Javax开头的包是Java的扩展包。
5.1 数字类(BigInteger和BigDecimal)
BigInteger
和BingDecimal
分别是大整数类跟大浮点数类。
有多大呢?你的内存有多大他就可以表示多大。
它们不支持+-*/
,但有共同的方法可以做加减乘除:
- add():加法操作
- subtract():减法操作
- multipy():乘法操作
- divide():除法操作
- max():求两者较大值
- min():求两者较小值
- equals():判断是否相等
- divideAndRemainder():求余数(返回的是数组)
BigInteger
import java.math.BigInteger
public class Test(){
public static void main(String args[]){
// 使用字符串创建,使用数字的话会有误差
BigInteger s1 = new BigInteger("987654321");
BigInteger s2 = new BigInteger("123456789");
System.out.println("加法操作:"+ s1.add(s2));
System.out.println("减法操作:"+ s1.subtract(s2));
System.out.println("乘法操作:"+ s1.multiply(s2));
// 【注意】只会保留整数
System.out.println("除法操作:"+ s1.divide(s2));
System.out.println("最大值:"+ s1.max(s2));
System.out.println("最小值:"+ s1.min(s2));
// 求余数
BigInteger result[] = s1.divideAndRemainder(s2);
System.out.println("商是:"+ result[0]+ "; 余数是:"+ result[1]);
}
}
BigDecimal
import java.math.BigDecimal
public class Test(){
public static void main(String args[]){
// 使用字符串创建,使用数字的话会有误差
BigDecimal s1 = new BigDecimal("987654321.123456789");
BigDecimal s2 = new BigDecimal("123456789.987654321");
System.out.println("加法操作:"+ s1.add(s2));
System.out.println("减法操作:"+ s1.subtract(s2));
System.out.println("乘法操作:"+ s1.multiply(s2));
// 【注意】可以指定小数点位数,以及舍入方法(这里是四舍五入)
System.out.println("除法操作:"+ s1.divide(s2, 6, RoundingMode.HALF_UP));
System.out.println("最大值:"+ s1.max(s2));
System.out.println("最小值:"+ s1.min(s2));
// 求余数
BigInteger result[] = s1.divideAndRemainder(s2,);
System.out.println("商是:"+ result[0]+ "; 余数是:"+ result[1]);
}
}
6. Java数据结构
6.1 数组
- 存放的数据都是同一种类型
- 所有数据线性排列
- 需明确容器长度
- 可通过位置索引快速定位数据
数组定义方式:
// 数组声明
int a[];
int[] b; // 推荐
// 数组初始化
b = new int[5]; // 开辟5个空间
// 声明的同时初始化
int[] b = new int[5];
int[] b = {1,2,3};
// 错误示范:int[5] b;
数组遍历:
【注意】数组的长度是length
属性;字符串的长度是**length()
方法**
int[] arr = new int[5];
// 第一种
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
// 第二种 不用担心数组越界
for(int i:arr){
System.out.println(i);
}
多维数组:
// 多维数组
int arrs[][] = new int[2][3]; // 规则数组,两行三列
int d[2][]; // 不规则数组
d[0] = new int[1]; //第一行只有一列
d[1] = new int[3]; // 第二行有三列
// 多维数组的遍历方式也有两种
// 第一种
for(int i=0;i<arrs.length;i++){ // 行数
for(int j=0;j<arrs[i].length;i++){ //列数
System.out.println(arrs[i][j]);
}
}
// 第二种
for(int[] arr:arrs){ // 行数
for(int a:arr){ //列数
System.out.println(a);
}
}
6.2 列表List
- 有序的Collection
- 允许重复元素
- 里面的每个数据都只能是对象,就算是由int类型它也会自动装箱。
主要实现:ArrayList(非同步)
、LinkedList(非同步)
、Vector(同步)
三者共同的方法:
- add([index], value):在index处添加value,如果没指定index则默认最后。
- remove(index):删除index处的元素
- get(index):获取index处的元素
- size():返回列表大小
三种遍历方法:Iterator(快)
、for+get方式(最慢)
、for-each(最快)
ArrayList
- 以可变数组实现的List,不支持同步
- 具有数组的特点。它不适合经常插入删除,主要用于查询数据。
- 跟数组相比可以动态扩容。容器每次满了的时候都会扩容一半。
创建:ArrayList<Integer> a = new ArrayLsit<Integer>();
ArrayList<Father> a = new ArrayLsit<Son>();
(多态)
LinkedList
- 以双向链表实现的List,不支持同步
- 顺序访问高效,插入删除高效
- 适合经常变化的数据
特有方法:
- addFirst(value):在头部添加数据
- getFirst(value):获取头部的数据
创建:LinkedList<Integer> linklist = new LinkedList<Integer>();
Vector
- 适合多线程
- 以可变数组实现的List
创建:Vector<Integer> linklist = new Vector<Integer>();
6.3 集合Set
- 确定性:对任何对象都能判定其是否属于某一个集合
- 互异性:每个元素是不一样的。(内容不一样)
- 无序性:集合内的元素无先后顺序
- 每个数据都只能是对象
主要实现:HashSet(非同步)
、LinkedHashSet(非同步)
、TreeSet(非同步)
三者共同的方法:
- add(value):增加一个元素。可以增加
null
元素(除了TreeSet) - clear():清空整个集合
- contains(value):判断是否包含value元素
- remove(value):删除value
- size():获取集合大小
- retainAll(Set2):计算两个集合的交集
HashSet
- 基于散列函数的集合
- 数据无先后顺序
创建:HashSet<Integer> hashset = new HashSet<Integer>();
LinkedHashSet
- 基于散列函数和双向链表的集合
- 元素有先后顺序
创建:LinkedHashSet<Integer> linkSet = new LinkedHashSet<Integer>();
TreeSet
- 基于树结构(TreeMap) 的集合
- 不可以容纳
null
- 元素自动排序,遍历输入的话会从小到大输出。
创建:TreeMap<Integer> treeMap = new TreeMap<Integer>();
HashSet
跟LinkedHashSet
判定重复元素的原则:
- 先判断两个元素的
hashCode
方法(计算对象信息和内存地址)的返回值是否相同,若不同返回false。 - 若相同,则使用
equals
方法判断,若不同返回false;否则返回true。
TreeSet
判断重复元素的原则是 使用compareTo
方法(需实现comparable接口)。
tips:
- hashCode和equals方法是在Object类中的,所以每个类都有。
- 使用equals判断相等的,那他们的哈希值(hashCode的返回值)一定相等
- 如果哈希值相等,equals判断的值未必相等。
6.4 映射Map
- 每个元素都是键值对,即{key, value}的形式,k都不允许为null且不能重复。
- 一个输入对应一个输入
主要实现:HashMap(非同步,快,数据量大)
常用方法:
- clear():清空数据
- contains(value):等同于containsValue(value),判断是否包含value
- containsKey(key):判断是否存在key
- get(key):获取该key的value
- put(key, value):新增或修改key
- remove(key):删除该k-v
- size():返回数据大小
HashMap
创建方式:HashMap<Integer,String> hashMap = new HashMap<Integer,String>();
6.5 工具类
6.5.1 Arrays类
- 处理对象是数组
- sort():对数组排序
- binarySearch():使用二分查找来查找元素。查找成功返回下标,失败返回 -1
- copyOf():批量复制
- fill():批量赋值
- equals():等价性比较
int[] a={3,1,2};
Arrays.binarySearch(a, 1); // 1
Arrays.sort(a);
int[] b = Arrays.copyOf(a, 2);
6.5.2 Collections类
- 处理对象基本为List
- 也有排序,查找,赋值
- 还可以查找集合的最大值最小值
- 反序排列