[Java]Java 基础语法和面向对象编程(OOP)
Posted Spring-_-Bear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Java]Java 基础语法和面向对象编程(OOP)相关的知识,希望对你有一定的参考价值。
一、基础语法
1. JDK、JVM 与 JRE 之间的关系
- JVM 是一个虚拟的计算机,具有指令集并使用不同存储区域。负责执行指令,管理数据、内存、寄存器,包含在 JDK 中
- JDK = JRE + Java 开发工具(java、javac、javap、javadoc ······)
- JRE = JVM + Java 核心类库
- JAVA_HOME =
D:\\Java\\jdk1.8.0_131
- Path =
%JAVA_HOME%\\bin
- CLASSPATH =
.;%JAVA_HOME%\\lib\\dt.jar;%JAVA_HOME%\\lib\\tools.jar;
2. 变量类型数据范围
- Java 数据类型中没有无符号类型
- 浮点数组成:符号位 + 指数位 + 尾数位
- Java 浮点类型默认为 double 类型,定义 float 类型时需要在末尾加上 F 或 f
- Java语言对布尔类型的存储并没有做规定,因为理论上存储布尔类型只需要
1 bit
,但是通常 JVM 内部会把 boolean 表示为4
字节整数
类型 | 字节数 | 范围 |
---|---|---|
byte | 1 | -128 - 127 |
short | 2 | -32768 - 32767 |
int | 4 | -231 - 231-1 |
long | 8 | -263 - 263-1 |
float | 4 | -3.403E38 - 3.403E38 |
double | 8 | -1.798E308 - 1.798E308 |
char | 2 | ISO 单一字符集 |
boolean | 1 | true or false |
3. 字符串与常用编码
- Java 在内存中总是使用
Unicode
表示字符,所以,一个英文字符和一个中文字符都用一个char
类型表示,它们都占用两个字节- Java 13开始,字符串可以用
"""..."""
表示多行字符串。多行字符串的排版不规则,则总是以最短的行首空格为基准- 注意要区分空值 null 和空字符串 “”,空字符串是一个有效的字符串对象,它不等于 null
- 使用
System.out.printf("%%");
输出 %
类型 | 字符占用字节数 | 汉字占用字节数 |
---|---|---|
ASCII | 1 | |
Unicode | 2 | 2 |
UTF - 8 | 1 | 3 |
GBK | 1 | 2 |
4. 类型转换
- 自动类型转换一:char -> int -> long -> float -> double
- 自动类型转换二:byte -> short -> int -> long -> float -> double
- (byte,short) 和 char 之间不会自动类型转换
- byte、char、short 在进行运算时,当作 int 处理
- char 类型可以保存 int 类型的常量值,但不能保存 int 型的变量值,需要强转
- 浮点数运算和整数运算相比,只能进行加减乘除这些数值计算,不能做位运算和移位运算
- 整数运算在除数为 0 时会报错,而浮点数运算在除数为 0 时,不会报错,但会返回以下几个特殊值(NaN:Not a Number、Infinity:无穷大、-Infinity:负无穷大)
- 如果强制类型转换后超过了整型能表示的最大范围,将返回整型的最大值
- 如果要进行四舍五入,可以对浮点数加上 0.5 再强制转型
5. 运算符
- 自增自减运算符的注意事项
int i = 1;
i = i++;
// output:1
System.out.println(i);
int i = 1;
i = ++i;
// output:2
System.out.println(i);
- 三元运算符的使用细节
Object object = true ? new Integer(1) : new Double(2.0);
// 三元运算符需要看作一个整体,故输出 1.0;if - else 分支结构则输出 1
System.out.println(object);
6. 运算符优先级
运算顺序 | 操作符 |
---|---|
. () ; , | |
R -> L | ++ – ~ ! |
L -> R | * / % |
L -> R | + - |
L -> R | << >> >>> |
L -> R | < > <= >= instanceof |
L -> R | == != |
L -> R | & |
L -> R | ^ |
L -> R | | |
L -> R | && |
L -> R | || |
L -> R | ? : |
R -> L | = *= /= %= |
R -> L | += -= <<= >>= |
R -> L | >>>= &= ^= |= |
7. 保留字
- strictfp:
strict float point
(精确浮点) ,strictfp关键字可应用于类、接口、方法。 使用 strictfp 关键字声明一个方法时,该方法中所有的 float 和 double 表达式都严格遵守 FP-strict 的限制,符合 IEEE-754 规范- volatile 是 jvm 提供的轻量级同步机制。作用是:、 保证可见性、禁止指令重排、不保证原子性
8. 位运算
- 算术右移(>>) :低位丢弃,符号位不变,高位补符号位
- 算术左移(<<):符号位不变,低位补0
- 无符号右移(>>):低位丢弃,高位补 0
9. switch
- switch 的 case 后跟常量或常量表达式,switch 中的表达式只能是以下类型中的一种:byte、short、char、int、enum、String
- 从 Java 12开始,switch 语句升级为更简洁的表达式语法,使用类似模式匹配(
Pattern Matching
)的方法,保证只有一种路径会被执行,并且不需要 break 语句- 大多数时候,在 switch 表达式内部,我们会返回简单的值。但是,如果需要复杂的语句,我们也可以写很多语句,放到 … 里,然后,用 yield 返回一个值作为 switch 语句的返回值
public class Main
public static void main(String[] args)
String fruit = "orange";
int opt = switch (fruit)
case "apple" -> 1;
case "pear", "mango" -> 2;
default ->
int code = fruit.hashCode();
yield code; // switch语句返回值
;
System.out.println("opt = " + opt);
10. 数组
-
数组是引用类型,创建数组时使用了 new 关键字
-
数组可以使用静态方式进行初始化
int[] array = 2,54,3,23;
- 创建数组后,若没有赋值,则有默认值
int[] a = new int[3];
// output: [0, 0, 0]
System.out.println(Arrays.toString(a));
- Java 中二维数组每行的元素个数可以不一致
int[][] array = new int[3][];
array[0] = new int[3];
array[1] = new int[2];
array[2] = new int[6];
- 遍历多维数组
int[][] array =new int[][]
1, 2, 3,
2, 3,
54, 234, 3, 131, 34
;
System.out.println(Arrays.deepToString(array));
11. 内部排序和外部排序
- 内部排序:将需要处理的所有数据都加载到内部存储器中进行排序(交换式排序法、选择式排序法和插入式排序法)
- 外部排序:数据量过大,无法全部加载到内存中,需要借助外部存储进行排序(合并排序法和直接合并排序法)
12. 内存布局
- 栈:一般存放基本数据类型(局部变量)
- 堆:存放对象(数组)
- 方法区:常量池(字符串)、类加载信息
13. 方法重载与可变参数
- 方法重载方法签名必须不同,返回值不是方法签名的一部分
- 可变参数的实参可以是 0 个或是多个,其本质就是数组
- 形参列表中可以同时有普通形参和可变参数,但必须保证可变参数是形参列表的最后一个参数
- 一个方法的形参列表只能出现一个可变参数
14. 作用域与构造器
- 类的属性有默认值,局部变量没有默认值
- 构造器完成对象的初始化,并不是创建对象;调用构造器时堆中已经分配了对象的空间,构造器只是负责对对象进行初始化
- 没有在构造方法中初始化字段时,引用类型的字段默认是 null,数值类型的字段用默认值,int类型默认值是 0,布尔类型默认值是 false
15. 对象创建流程和 this 关键字
- 加载类信息,生成 .class 对象
- 在堆中为对象分配空间
- 完成对象的默认初始化
- 构造器完成对象的初始化
- JVM 会给每个对象分配 this,代表当前对象;哪个对象调用,this 就指向它
- this 不能在类定义的外部使用,只能在类定义的方法中使用
二、面向对象
1. 继承内存布局
- 子类继承了父类所有的方法和属性,但父类的私有属性不能在子类中直接访问
- 当创建子类的对象时,无论使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类中使用 super 去显式调用父类的构造器完成父类的初始化工作,否则编译不会通过
- super() 和 this() 都只能放在构造器的第一行,因此这两个方法不能同时存在一个构造器中
- 从 Java 15开始,允许使用 sealed 关键字修饰 class,并通过 permits 明确写出能够从该 class 继承的子类名称
instanceof
实际上判断一个变量所指向的实例是否是指定类型,或者这个类型的子类- 从 Java 14 开始,判断 instanceof 后,可以直接转型为指定变量,避免再次强制转型
2. Object 类的所有方法
3. super 和 this 的区别
- super 代表父类的引用,用于访问父类的属性、方法、构造器
- 使用 super 不能访问父类的 private 成员
- this 关键字可以用户调用本类的构造器以及执行调用的对象
区别点 | this | super |
---|---|---|
方法 | 访问本类属性,没有则依次查找父类 | 直接依次查找父类属性 |
属性 | 访问本类方法,没有则依次查找父类 | 直接依次查找父类方法 |
构造器 | 行首调用本类其它构造器 | 行首调用父类指定构造器 |
特殊 | 表示当前对象 | 表示子类中访问父类的对象 |
4. 方法重写与重载
- 子类重写父类的方法时,返回类型可以是父类返回类型的子类
- 子类不能缩小父类方法的访问权限
- 重载与重写也体现了多态
5. 动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的内存地址 / 运行类型动态绑定
- 当调用对象属性时,不存在动态绑定,在哪儿声明在哪使用
- 多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法
6. == 和 equals()
作用 | |
---|---|
== | 判断基本类型时判断值是否相等;判断引用类型时判断是否引用到同一个对象 |
equals() | 只能判断引用类型是否引用到同一个对象,子类往往重写 |
7. Object 类的 toString()、equas() 方法
public String toString()
return getClass().getName() + "@" + Integer.toHexString(hashCode());
public boolean equals(Object obj)
return (this == obj);
8. 类变量
- 类变量也即静态变量,类方法也即静态方法
- JDK 8 以前类变量放在方法区的静态域中,JDK 8 以后放在堆的对应类的 class 对象尾部
- 当方法中不设计到与任何对象相关的成员时,则可以将方法设计为静态方法以提高运行效率
- 静态代码块、静态方法只能调用静态成员;普通方法和普通代码块可以调用任何成员
- 因为静态方法属于 class 而不属于实例,因此,静态方法内部,无法访问 this 变量,也无法访问实例字段,它只能访问静态字段
9. 代码块和类加载
- 代码块可以理解为只有方法体的方法,它没有方法名、返回值、参数,不能通过对象或类显式调用,只有在加载类或是创建对象时隐式调用
- 代码块的修饰符只能是 static 或无修饰符,方法体中的语句可以是任何正确逻辑语句
- 类代码块的调用顺序优先于构造器
- 类加载时机:创建该类的对象时、创建子类的对象时父类也会加载、使用类的静态成员时
- 如果只是调用类的静态成员,普通代码块并不会执行,普通代码块可以看作是构造器的一种补充机制,所以在调用类的静态成员但没有创建类的对象时,构造器并未被调用,因而类的普通代码块不会被执行
class GirlFriend
public static final int AGE = 2;
// 当使用到 GirlFriend.AGE 时,此静态代码块不会执行即此类未加载
static
System.out.println("I am your only girlfriend");
10. Java 代码执行顺序
- 父类的静态代码块和静态属性按定义顺序依次执行
- 子类的静态代码块和静态属性按定义顺序依次执行
- 父类的普通代码块与普通属性按定义顺序依次执行
- 父类的构造器
- 子类的普通代码块与普通属性按定义顺序依次执行
- 子类的构造器
11. 单例设计模式
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。设计模式好比经典的棋谱,不同的棋局采用不同的棋谱,免去再次思考和摸索的过程
- 类的单例设计模式:采取一定的方法在整个软件系统中,某个类有且仅有一个对象实例,并且该类只提供一个取得该对象的方法
- 单例模式的实现:构造器私有化、在类的内部创建对象、向外部提供一个静态的公共方法取得该对象
- 饿汉式与懒汉式的区别:创建时机不同,饿汉式在类加载是就创建了对象而懒汉式在使用到对象时才创建
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题
// 单例设计模式饿汉式
class GirlFriend
private GirlFriend()
private static final GirlFriend girlFriend = new GirlFriend();
public static GirlFriend getGirlFriend()
return girlFriend;
// 单例设计模式懒汉式
class GirlFriend
private GirlFriend()
private static GirlFriend girlFriend;
public static GirlFriend getGirlFriend()
if (girlFriend == null)
girlFriend = new GirlFriend();
return girlFriend;
java.lang.Runtime
类就是经典的懒汉式单例模式
public class Runtime
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime()
return currentRuntime;
/** Don't let anyone else instantiate this class */
private Runtime()
12. final
- final 的使用场景:不希望某个类被继承、不希望父类的方法被子类重写、不希望类的成员值被修改、不希望局部变量的值被修改
- final 修饰的属性必须初始化,此后不能更改,初始化可在三个地方进行:定义时初始化、构造器中初始化、代码块中初始化
- 如果 final 修饰的属性同时是 static 的,那么只能在两个地方初始化:定义时初始化、静态代码块中初始化
- 如果非 final 类中含有 final 修饰的方法,则该方法可以被继承不能被重写
- 八大包装类都是 final 类型的
- final 往往和 static 搭配使用,效率更高,因为底层编译器做了优化处理,不会导致类的加载
13. 抽象类
- 当父类的某些方法需要被声明,但又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
- 有抽象方法的类一定是抽象类,抽象类不一定有抽象方法,abstract 只可以修饰类和方法
- 如果一个类继承自抽象类,则必须实现父类的所有抽象方法,除非它也是一个抽象类
- 抽象方法不能使用 private、final、static 来修饰,因为这些修饰符与重写相违背
14. 接口
- JDK 7 及之前的版本,所有的方法都没有方法体即是抽象方法
- JDK 8 及之后的版本,接口类中可以有静态方法、默认方法即接口中的方法可以有具体实现
- 抽象类实现接口可以不用实现方法;接口继承接口时可以不用实现方法
- 接口中的属性能且仅能是 public static final 修饰符
- 继承的价值:解决代码的复用性与可维护性
- 接口的价值:设计规范方法,一定程度上实现代码解耦即接口规范性 + 多态
- Java 的接口特指
interface
的定义,表示一个接口类型和一组方法签名,而编程接口泛指接口规范,如方法签名,数据格式,网络协议等
interface Study
default public void english()
System.out.println("Study english");
public static void math()
System.out.println("Study math");
15. 内部类
- 局部内部类:局部内部类定义在外部类的局部位置,比如方法或代码块中,并且有类名,有以下几个特点:
- 可以直接访问外部类的所有成员,包含私有的
- 不可以添加访问修饰符,可以添加 final
- 局部内部类可以理解为方法的局部变量
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则。如果想访问外部类的成员,可以使用 外部类名.this.成员 的方式去访问
class Outer
private String name = "Spring-_-Bear";
public void m1()
class Inner
private String name = "springbear";
private void test()
// 调用局部内部类的成员
System.out.println(name);
// 调用外部类的成员
System.out.println(Outer.this.name);
// 调用局部内部类的方法
new Inner().test();
- 匿名内部类:匿名内部类定义在外部类的局部位置,比如方法中,并且没有类名,同时还是一个对象
- 匿名内部类本身既是一个类的定义,同时它本身也是一个对象,因而从语法层面看,它既有类定义类的特征也有创建对象的特征
- 只能定义在方法或代码块中,匿名内部类的地位就好比一个没有名字的局部变量
- 经典使用场景:将匿名内部类直接当作实参传递,简洁高效
/**
* 基于接口的匿名内部类
*
* @author Spring-_-Bear
* @date 2021-12-12 08:50
*/
public class C02
public static void main(String[] args)
// 方式1
I03 i03 = new I03()
@Override
public void cry()
System.out.println("呜呜呜~~~");
;
i03.cry();
// 匿名内部类的类名:I03
System.out.println(i03.getClass());
// 方式2
new I03()
@Override
public void cry()
System.out.println("呜呜呜~~~");
.cry();
interface I03
void cry();
- 成员内部类:成员内部类可以理解为类的成员
/**
* 成员内部类的使用
*
* @author Spring-_-Bear
* @date 2021-12-12 08:50
*/
public class C02
public static void main(String[] args)
new Outer().new Inner().m();
class Outer
private String name = "Spring-_-Bear";
// 成员内部类
public class Inner
public void m()
System.out.println(name);
- 静态内部类
- 静态内部类可以直接访问外部类的所有静态成员,但不能访问非静态成员
- 静态内部类可以理解类的静态成员
以上是关于[Java]Java 基础语法和面向对象编程(OOP)的主要内容,如果未能解决你的问题,请参考以下文章