java 零基础学习
知识点 | 链接 |
---|---|
语法语义关键字、方法定义重载、封装继承覆盖等基础知识 | java零基础从入门到精通(全) |
接口、抽象类、数组、常用类以及异常等 | javaSE从入门到精通的二十万字总结(一) |
集合与IO流 | javaSE从入门到精通的二十万字总结(二) |
线程、反射机制以及注解 | javaSE从入门到精通的二十万字总结(三) |
1. JDK/JRE/JVM区别
JDK = JRE + 开发工具集(例如Javac编译工具等)
JRE = JVM + Java SE 标准类库
jdk适合编写的时候,jre适合编译的时候
- JRE:java运行环境
- JDK:Java开发工具箱
- JVM:java虚拟机
JVM的内存结构中三块比较重要的内存空间
- 方法区:存储代码片段,存储xxx.class字节码文件,类加载器将代码加载到这
- 堆内存:面向对象
- 栈内存:所需要的内存空间(局部变量)
2. 构造方法
调用构造方法来完成对象的创建,以及对象属性的初始化操作
一个类中可以定义多个构造方法,这些构造方法构成方法重载
3. 重载
一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数
和返回值类型(void/int)无关,和修饰符列表(public static)无关
4. 面向对象
面向对象具有三大特征
- 封装
- 继承
- 多态
4.1 封装
- 属性私有化(使用private关键字进行修饰。)
- 对外提供简单的操作入口
(1个属性对外提供两个set和get方法。外部程序只能通过set方法修改,只能通过get方法读取,可以在set方法中设立关卡来保证数据的安全性)
4.2 继承
- 有了继承之后才会衍生出方法的覆盖和多态机制
- java 中的继承只支持单继承,不支持多继承,但支持间接继承,例如:class C extends B,class B extends A。C++中支持多继承
- 子类继承父类,除构造方法和被 private 修饰的数据不能继承外,剩下都可以继承
执行顺序:
先调用父类的静态代码块,子类静态代码块
在执行父类的代码以及构造函数从上往下,最后执行子类的代码块和构造函数
4.3 多态
多态存在的三个必要条件
- 继承
- 方法覆盖
- 父类型引用指向子类型对象
对于多态的理解一定要分为编译阶段和运行阶段来进行分析,编译阶段只是看父类型中是否存在要调用的方法,如果父类中不存在,则编译器会报错,编译阶段和具体 new 的对象无关。但是在运行阶段就要看底层具体 new的是哪个类型的子对象了,new的这个子类型对象可以看做“真实对象”,自然在运行阶段就会调用真实对象的相关方法。例如代码:Animal a = new Cat(); a.move();,在编译阶段编译器只能检测到a的类型是Animal,所以一定会去Animal类中找move()方法,如果 Animal 中没有 move()方法,则编译器会报错,即使 Cat 中有 move()方法,也会报错,因为编译器只知道 a 的类型是 Animal 类,只有在运行的时候,实际创建的真实对象是 Cat,那么在运行的时候就会自动调用 Cat 对象的 move()方法。这样就可以达到多种形态,也就是说编译阶段一种形态,运行阶段的时候是另一种形态
5. 关键字
5.1 static
-
java语言中凡是用 static 修饰的都是类相关的,不需要创建对象,直接通过“类名”即可访问,即使使用“引用”去访问,在运行的时候也和堆内存当中的对象无关
-
实例变量必须先创建对象,通过“引用”去访问,而静态变量访问时不需要创建对象,直接通过“类名”访问。实例变量存储在堆内存当中,静态变量存储在方法区当中。实例变量在构造方法执行过程中初始化,静态变量在类加载时初始化
-
当一个类的所有对象的某个“属性值”不会随着对象的改变而变化的时候,建议将该属性定义为静态属性(或者说把这个变量定义为静态变量),静态变量在类加载的时候初始化,存储在方法区当中,不需要创建对象,直接通过“类名”来访问
this 和 super 都是无法使用在静态方法当中的,必须适用对象创建
5.2 final
定义的东西都不能修改,类方法变量,如下
- final 修饰的类不能被继承
- final 修饰的方法不能被覆盖
- final 修饰的变量不能被修改
如果修饰的引用,那么这个引用只能指向一个对象,也就是说这个引用不能再次赋值,即地址已经确定了不能修改,但被指向的对象(属性值)是可以修改的
final的对象是实例变量,放在堆中,会占用空间,可以结合static,节省空间,放在常量(常量和静态变量都存在方法区,都在类加载的时候初始化)
6. 抽象类
抽象方法不能被 final 修饰,因为抽象方法就是被子类实现的
- 抽象的方法只需在抽象类中,提供声明,不需要实现
- 如果一个类中含有抽象方法,那么这个类必须定义成抽象类。抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中
一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写/实现
7. 接口
类与类是继承extends,类与接口是实现implements,其实都是继承
- 完全抽象的,支持多继承,且一个接口可以继承多个接口
- 所有的元素都是public修饰的,抽象方法的public abstract可以省略,常量的public static final可以省略,方法不能有方法体
当一个非抽象的类实现接口的话,必须将接口中所有的抽象方法全部实现(覆盖、重写)
抽象类和接口的区别:
-
抽象类是半抽象的,有构造方法,只允许出现常量和抽象方法。类和类之间只能单继承,一个抽象类只能继承一个类(单继承)。
-
接口是完全抽象的,没有构造方法,接口和接口之间支持多继承,一个类可以同时实现多个接口
比较内容 | 抽象类 | 接口 |
---|---|---|
构造方法 | 可以有 | 不可以有 |
方法 | 可以有抽象方法和普通方法 | 只能有抽象方法,但1.8版本之后可以有默认方法 |
实现 | extend | implments |
修饰符 | public、default、protected | 默认public |
变量 | 可以有常量也可以有变量 | 只能是静态常量默认有public static final修饰 |
多继承 | 单继承 | 多个接口 |
静态方法 | 可以有 | 不可以 |
8. object类
protected Object clone()
// 负责对象克隆的。int hashCode() /
/ 获取对象哈希值的一个方法。boolean equals(Object obj)
// 判断两个对象是否相等String toString()
// 将对象转换成字符串形式protected void finalize()
// 垃圾回收器负责调用的方法
9. String/StringBuilder/StringBuffer
String为什么是不可变的:源代码String类中有一个byte[]数组,这个byte[]数组采用了final修饰,因为数组一旦创建长度不可变。并且被final修饰的引用一旦指向某个对象之后,不可再指向其它对象,所以String是不可变的!“abc” 无法变成 “abcd”
- StringBuffer中的方法都有:synchronized关键字修饰。表示StringBuffer在多线程环境下运行是安全的。
- StringBuilder中的方法都没有:synchronized关键字修饰,表StringBuilder在多线程环境下运行是不安全的。
源代码StringBuffer/StringBuilder内部实际上是一个byte[]数组, 这个byte[]数组没有被final修饰,StringBuffer/StringBuilder的初始化容量我记得应该是16,当存满之后会进行扩容,底层调用了数组拷贝的方System.arraycopy()…是这样扩容的。所以StringBuilder/StringBuffer适合于使用字符串的频繁拼接操作
10. 自动装箱和自动拆箱
- 自动装箱:基本数据类型自动转换成包装类。
- 自动拆箱:包装类自动转换成基本数据类型。
补充:
java中为了提高程序的执行效率,将[-128到127]之间所有的包装对象提前创建好,放到了一个方法区的“整数型常量池”当中了,目的是只要用这个区间的数据不需要再new了,直接从整数型常量池当中取出来
所以
Integer a = 128;
Integer b = 128;
System.out.println(a == b); //false
Integer x = 127;
Integer y = 127;
// == 永远判断的都是两个对象的内存地址是否相同。
System.out.println(x == y); //true