一、标识符规范
标识符必须以字母(汉字)、下划线、美元符号开头,其他部分可以是字母、下划线、美元符号,数字的任意组合。谨记不能以数字开头。java使用unicode字符集,汉字也可以用该字符集表示。因此汉字也可以用作变量名。
关键字不能用作标识符。
类名首字母需大写(Welcome)、方法和变量名遵循驼峰原则 snRsfService()
二、数据类型
java的数据类型由8种基本数据类型和多种引用数据类型构成。
- 基本数据类型
1.1数值型
数值型又分为整数类型和浮点类型。
java
long l = 10000000000L; //整型数据默认是int, 转成long后面要加 L 或 l
float f = 3.14F; //浮点常量默认是double,改成float后面要加F 或 f
<一些可有可无的科普:计算机底层浮点数表示方法>
在计算机底层float和double都是由IEEE754标准进行储存的,该标准将浮点数分成符号位、指数位、尾数位。分配如下:
单精度浮点数float:32bit(4字节)
1bit(符号位)
8bits(指数位)
23bits(尾数位)
双精度浮点数double:64bit(8字节)
1bit(符号位)
11bits(指数位)
52bits(尾数位)
数据的表示范围是由指数的位数来决定的。float的指数位有8位,能表示0-255一共256个状态。加之IEEE标准规定有127的偏移量,float的指数最大值为128。float的范围为-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38;double范围的计算方法和float同理。double的范围为-2^1024 ~ +2^1024,也即-1.79E+308 ~ +1.79E+308。
精确度是由尾数的位数来决定的。float的尾数位有23位,能表示最大7位十进制数字,但实际精度取决于浮点数的具体值。double的尾数位有52位,能表示最大16位十进制数字,但实际精度同样取决于浮点数的具体值。
(尽量不要在比较和金额计算中使用浮点数,精确计算用BigDecimal)
1.2 字符型
内存中占2个字节,unicode就是用char表示的
1.3 布尔型
在内存中占1 或4个字节(JVM用int表示bool,CPU层面有利于高效存取)
- 引用数据类型
类(Class):自定义的类。类是对象的模板,它定义了对象的属性和方法。
接口(Interface):一种完全抽象的类,它只包含常量和方法的声明,没有实现。接口可以被类实现,以实现特定的行为。
数组(Array):一种数据结构,用于存储固定大小的同类型元素集合。
字符串(String):一种特殊的对象,用于表示文本数据。Java中的字符串是不可变的,一旦创建就不能修改其内容。
包装类(Wrapper Classes):基本数据类型的封装类,如Integer、Double、Character等。它们提供了基本数据类型到对象类型的桥梁,并提供了额外的功能。
其他自定义对象:除了上述提到的类型外,还包括用户自定义的类、枚举(Enum)、注解(Annotation)等。
三、关键字
- static
static修饰变量:静态变量又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量 ,可以直接通过类名来访问它。静态变量在内存中只存在一份。
static修饰函数:静态方法在类加载的时候就存在 了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。
static修饰代码块:静态代码块在类构造函数之前被执行。
- final
final修饰变量:修饰基本数据类型时,表示这个变量值不可变。
final修饰引用数据类型:表示这个变量引用不可变,但引用的对象具体内容是可以改变的(比如对象的某个属性值是可变的)。
final修饰方法:表示此方法不可被子类重写。
final修饰类:表示此类不可以被继承。
- this
this的本质就是当前对象的地址。普通方法中,this指向调用该方法的对象。构造方法中,指向正要初始化的对象。
java
class User{
int id;
String name;
String pwd;
User(){}
public User(int id, String name){
this.id = id;
this.name = name;
}
public User(int id, String name, String pwd){
this(id, name);//构造方法重载,该种调用必须位于构造方法的第一行
this.pwd = pwd;
}
}
- super
super可以看做父类对象的引用,但是创建子类对象的过程并没有产生父类对象,super代表当前对象的父类型特征。在一个类中,如果构造方法的第一行代码没有显式的使用super()或this(),那么java都会默认调用super()
- String StringBuilder StringBuffer
String表示字符串,从jdk1.0开始就出现了。从Java7版本开始存在于堆中的字符串常量池中。其底层使用一个数组进行存放,在JAVA8中是一个char数组,而在JAVA9中是一个byte数组,并且这个数组使用final进行修饰,表示不可变的。所以String类是天生线程安全的。
StringBuffer是为了解决String类在内容频繁变化时,性能不高这个问题。表示的是一个可变长度字符串,他也是在Jdk1.0中就出现了,其内部的方法都是被Synchronized修饰,所以他是线程安全的。并且在stringBuffer有个缓存数组一直缓存变化着的内容(缓存最后一个值)。它的toString方法的返回值就是这个缓存数组的String对象。这一点和stringBuilder不一样,stringbuilder的tostring返回值是通过存储他的那个数组创建的。
虽然说这一点不同StringBuilder也是表示可变长度字符串。他是从JDK1.5开始出现的,其方法没有被Synchronized修饰,是非线程安全的,但它的性能相对于StringBuilder更高。
四、方法与对象
- 方法重载的条件
形参类型,个数,顺序不同即可构成方法重载。只有返回值不同不构成方法重载,会报编译错误。
- 对象实例化的方式
2.1 new
2.2 clone()
2.3 通过反射机制创建
java
//用 Class.forName方法获取类,在调用类的newinstance()方法
Class<?> cls = Class.forName("com.dao.User");
User u = (User)cls.newInstance();
2.4 序列化反序列化
java
//将一个对象实例化后,进行序列化,再反序列化,也可以获得一个对象(远程通信的场景下使用)
ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream("D:/data.txt"));
//序列化对象
out.writeObject(user1);
out.close();
//反序列化对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/data.txt"));
User user2 = (User) in.readObject();
System.out.println("反序列化user:" + user2);
in.close();
- 对象复制的方式
通过构造方法、通过对象的值、通过object的clone方法。
java
//通过构造方法
TestClone tc = new TestClone();
tc.name = 123;
TestClone tc2 = new TestClone(tc);
//通过对象的值
直接将老对象的值挨个复制给新对象即可
//通过object的clone方法
通过该方式复制对象时必须重写cloneable的clone方法。
- 深拷贝与浅拷贝的区别
浅拷贝:仅仅克隆基本类型变量,不克隆引用类型变量;
深拷贝:既克隆基本类型变量,又克隆引用类型变量;
五、面向对象特性
- 封装
将对象的属性包裹起来,并通过接口控制访问。高内聚低耦合,并隐藏实现细节,安全性高。
- 继承
java类没有多继承,即子类只能继承一个父类,接口有多继承。子类继承父类可以得到除构造方法外的全部非私有属性和方法。java.lang.Object是所有类的父类。
java
//instanceof 判断对象是否是某个类的实例对象
Stu stu = new Stu();
System.out.print(stu instanceof Stu);
- 多态
同一个方法调用,由于对象不同,可能有不同的行为。多态是方法的多态不是属性的多态。优点是简化灵活,替换性强,可拓展性高。
三个必要条件是重写,继承和父类引用指向子类对象。
当指向子类对象的父类引用,调用子类重写的父类方法时,多态就出现了。
java
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
六、抽象类
使用abstract修饰的类是抽象类,包含抽象方法的类是抽象类。
java
//模板方法模式(抽象类的应用)
public abstract class DBOperator {
//1. 建立连接 2. 打开数据库 3. 使用数据库 4. 关闭连接
public abstract void connection();
public void open(){
System.out.println("打开数据库");
}
public void use(){
System.out.println("使用数据库");
}
public void close(){
System.out.println("关闭连接");
}
public void process(){
connection();
open();
use();
close();
}
public static void main(String[] args) {
// new MySqlOperator().process();
new OracleOperator().process();
}
}
class MySqlOperator extends DBOperator {
@Override
public void connection() {
System.out.println("建立和Mysql数据库的连接");
}
}
class OracleOperator extends DBOperator {
@Override
public void connection() {
System.out.println("建立和Oracle数据库的连接");
}
}
七、接口
由全局常量(public static final 可省略)、抽象方法(public abstract)、默认方法(public default)、静态方法(public static)组成,其中方法的public可省略。java9中接口可以包含只能被接口内部调用的私有方法。
常量:接口中可以定义常量,这些常量默认是public static final的,即它们是公共的、静态的、不可变的。接口不能包含变量。
抽象方法:接口中可以包含抽象方法,这些方法只有方法签名,没有方法体。抽象方法默认是public abstract的,即它们是公共的、抽象的。实现接口的类必须提供接口中所有抽象方法的具体实现。
默认方法:这些方法有方法体,因此可以提供默认实现。可以被重写。
静态方法:这些方法有方法体,可以直接通过接口名调用,而不需要实现类的实例。静态方法使用static关键字定义,实现类不能重写接口的静态方法。静态方法只能调用接口中的其他静态方法或私有静态方法。
命名冲突问题:
对于一个实现了多个接口且继承自某一个父类的子类,如果方法名和形参列表相同,会发生命名冲突。若父类的方法和接口默认方法名冲突,则父类优先。若多个接口默认方法导致命名冲突,则子类需重写该方法。
八、内部类
为了方便使用外部类相关的属性和方法,通常会在开发中定义一个内部类。
内部类可以直接访问外部类的私有属性,但外部类不能访问内部类的属性。内部类被当成外部类的成员。SubList就是ArrayList的内部类。
java
//非静态内部类初始化
Outer.Inner inner = new Outer().new Inner();
//静态内部类初始化
Outer.Inner inner = new Outer.Inner();
//匿名内部类,适合只使用一次的类
/**
目标:掌握匿名内部类的使用形式
*/
public class Test {
public static void main(String[] args) {
//使用匿名内部类替代学生类,创建的类型是new的那个类的子类
Swimming s = new Swimming() {
@Override
public void Swim() {
System.out.println("学生游的很快");
}
};
go(s);
}
/**
方法:学生、老师、运动员可以一起游泳
*/
public static void go(Swimming s)
{
System.out.println("开始游泳了~~");
s.Swim();
System.out.println("结束游泳了~~");
}
}
//创建学生类
/*class Student implements Swimming{
@Override
public void Swim() {
System.out.println("学生游的很快");
}
}*/
//定义一个接口规范游泳
interface Swimming{
void Swim();
}
九、包装类
包装类与基本数据类型的转换过程就是java的自动装箱与拆箱,在性能敏感的应用中,过度使用会导致性能下降,因为需要额外的内存分配和垃圾回收。
java
Integer ele = new Integer(10);
int ele3 = ele.intValue();
//自动装箱
Integer ele2 = 10;
//自动拆箱
int ele4= ele;
//Integer包装类缓存范围[-128,127]
Integer a = 100;
Integer b = 100;
System.out.println(a==b);//true
Integer a1 = 200;
Integer b1 = 200;
System.out.println(a1==b1);//false
<一些不必要的知识点:包装类默认缓存范围>
Boolean:true和false
Integer:-128~127
Byte:-128~127
Character:0~127
Short:-128~127
Long:-128~127
Float和Double无缓存
十、异常机制
java中的异常都继承自Throwable类,分为Error和Exception。Error表示编译或系统错误,如OOM和StackOverFlow。Exception表示程序可以捕获或处理的异常,又分为运行时异常和非运行时异常。
RuntimeException 是运行时异常,这类异常是不受检查的,需要程序员自己决定要不要做处理。RuntimeException 以外的异常都属于非运行时异常。例如 IOException、ClassCastException 等以及用户自定义的 Exception 异常等。
<一些不必要的知识点:try-cache-finally 与 try-with-resouce>
前者中的finally一定会被运行,但遇见如下情况不会执行:比如退出程序,finally 语句块中发生了异常,还有程序所在的线程死亡。try-with-resouce可以保证运行结束后每个资源都被自动关闭。任何实现了 java.lang.AutoCloseable 的对象, 包括所有实现了 java.io.Closeable 的资源对象, 都可以用它进行关闭。
java
public class MyAutoClosable implements AutoCloseable {
public void doIt() {
System.out.println("MyAutoClosable doing it!");
}
@Override
public void close() throws Exception {
System.out.println("MyAutoClosable closed!");
}
public static void main(String[] args) {
try(MyAutoClosable myAutoClosable = new MyAutoClosable()){
myAutoClosable.doIt();
} catch (Exception e) {
e.printStackTrace();
}
}
}
十一、泛型
(待施工)