java基础语法
基础语法
堆,栈,常量池,内容存放的位置等
DOS命令
一些小函数
getClass()
查看运行类型
StringBuffer
类似vector的不定长度的String
length() 实际长度
capacity() 理论容量
String a; String[] res = a.split(";");
String.substring(a,b)
[a,b) 截取字符串子串
Arrys.sort()
Math.round()
四舍五入的求整数
返回类型为long int a = (int) Math.round(x);
Math.abs()
Math.random()
Math.random() //随机double类型浮点数 (0.0 ~ 1.0)
Random类
Random r = new Random();
int randomNum = r.nextInt(3); //随机生成[0,3)的一个随机整数
instanceof
用来判断一个对象是否是一个类或该类子类的实例
注意判断的是运行类型
如
class A{}
class B extends A{}
B b = new B();
A a = nwe B();
A aa = new A();
//判断的是运行类型
(aa instanceof B) == false;
(b instanceof B) == true;
(b instanceof A) == true;
(a instanceof B) == true;
(a instanceof A) == true;
高精度类型
整形高精度 BigInteger 、浮点高精度 BigDecimal
基础数据类型转换成高精度可以用 valueOf()
在对这些高精度进行运算的时候不能使用 + 、-、*、/
而是运用对应的方法 add()、substract()、multiply()、divide()、mod()
判断是否相等用 compareTo()
包引入 like 头文件
import java.util.Scanner;
基础变量类型的不同
float
java的浮点数默认是double 定义float(要加f或者F) 如下: float test = 1.1f;
bool && boolean
cpp 是 bool java是 boolean
注意java中不能用int代替bool
比如 int a = 1;if(a){}; 会报错:int类型不能用作bool量
自动类型转换
当进行多种数据类型的混合运算时,会转换成容量最大的类型
从小到大为 : char -> int -> long -> float -> double && byte -> short -> int
byte short char 不会互相转换而是直接转成int类型
同样的,大容量向低容量转换可能会损失精度
对Sring类型的转换
注意cpp 的 string s小写 java 的 String S大写
基础数据类型转换成String
int a = 123; String b = a + "";
拓展:toString() 函数
string 类型对其他基础数据类型的转换
String s = "123";
int num1 = Integer.parseInt(s);
float num2 = Float.parseFloat(s);
double num3 = Double.parseDouble(s);
//对char的转换
s.charAt(0); //得到字符串的第一个字符,以此类推
逻辑运算符
^ 异或
| 逻辑或 (与 || 的不同 不管第一个是否为 true 都会判断第二个 效率慢)
& 逻辑与 (与 && 的不同 不管第一个是否为 false 都会判断第二个 效率慢)
没有 <<< , 且 >>> 用0填充高位,>>用符号位填充高位
java的输入
- nextLine() 读入一行
- next() 读入下一个单词
- nextInt() 读入下一个int
- nextDouble() 读入下一个double
- hasNext() 是否还有下一个输入,返回Boolean
- hasNextInt()
- hasNextDouble()
for_each
int[] a = {2,1,3,5,4};
for(int i : a) System.out.println(i);
跳转控制语句break
一般和goto一样,不在开发中使用,但作为一个完整的知识点还是需要了解。
label1:
for(int i = 1;i <= 10;i ++){
System.out.println("i = " + i);
label2:
for(int j = 1;j <= 10;j ++){
System.out.println("j = " + j);
if(j == 4){
System.out.println("break");
break label1;
}
}
}
System.out.println("label * " + i);
数组
数组的初始化
最好是写成 int[] a 这种类型,可以更清楚的说明该变量是数组类型
变量类型 + [] 数组长度可以用数组名.length来得到
允许存在匿名数组 new int[] = {1,2,3};
int[] a 和 int a[] 都可以
int n;
Scanner cin = new Scanner(System.in);
n = cin.nextInt();
int a[] = new int[n]; //动态数组
int b[] = {1,2,3}; //初始化数组
b = new int[] {2,3,4}; //匿名数组赋值,不用创建新变量
for(int i = 0;i < n;i ++) a[i] = cin.nextInt();
//可以先定义后开大小
int c[];
for(int i = 0;i < n;i ++){
if(i == 2){
c = new int[i];
for(int j = 0;j < i;j ++) c[i] = cin.nextInt();
}
}
数组的赋值
类指针的指向、引用操作
//类似指针,不是数组拷贝
int a[] = {1,2,3};
//这种类型的赋值是引用赋值,当 c 改变时 a 也会改变,cpp不能采用此种赋值
int c[];
c = a;
for(int i = 0;i < c.length;i ++) System.out.println(c[i]);
c[0] = 66;
for(int i = 0;i < c.length;i ++) System.out.println(a[i]);
数组的拷贝
int a[] = {1,2,3};
int b[] = new int[a.length];
for(int i = 0;i < a.length;i ++) b[i] = a[i];
二维数组
int a[][] 或 int[] a[] 或 int[][] a
//静态初始化
int [][] a = {{1,2,3},{4,5,6},{7,8,9}};
for(int i = 0;i < a.length;i ++) //有几个a[i]
for(int j = 0;j < a[i].length;j ++) //a[i]有几个元素
System.out.println(a[i][j]);
//动态定义行和列
Scanner cin = new Scanner(System.in);
int a[][] = new int[3][3];
for(int i = 0;i < 3;i ++)
for(int j = 0;j < 3;j ++)
a[i][j] = cin.nextInt();
//只能动态定义列而不是行
Scanner cin = new Scanner(System.in);
int a[][] = new int[3][];
for(int i = 0;i < 3;i ++){
int temp = cin.nextInt();
a[i] = new int[temp];
for(int j = 0;j < temp;j ++) a[i][j] = cin.nextInt();
}
for(int i = 0;i < 3;i ++) {
for (int j = 0; j < a[i].length; j++) {
System.out.print(a[i][j] + " ");
}
System.out.println();
}
//引用也类似
int a[][] = {{1,2},{3,4}};
int b[][] = a;
b[1][1] = 99;
for(int i = 0;i < 2;i ++)
for(int j = 0;j < 2;j ++)
System.out.println(a[i][j]);
面向对象
jvm的内存对象存在形式
栈 -> 堆 -> 基础数据类型就直接放在堆中,否则放在堆中存首地址、信息存方法池中的常量池
类的和方法
类和方法的构造
实例
import java.util.Scanner;
public class t1 {
public static void main(String[] srg){
Scanner cin = new Scanner(System.in);
System.out.println("Please input a && b :");
int a = cin.nextInt(),b = cin.nextInt();
fun xixi = new fun();
xixi.build(a,b);
xixi.show();
}
}
class tool{
public void print(int map[][]){
for(int i = 0;i < map.length;i ++)
for(int j = 0;j < map[i].length;j ++)
System.out.print(map[i][j] + " " + (j == map[i].length - 1 ? "\n" : ""));
}
}
class fun{
Scanner cin = new Scanner(System.in);
int arr[][];
public void build(int a,int b){
arr = new int[a][b];
for(int i = 0;i < a;i ++)
for(int j = 0;j < b;j ++)
arr[i][j] = cin.nextInt();
System.out.println("Build success");
}
public void show(){
for(int i = 0;i < arr.length;i ++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
tool temp = new tool();
temp.print(arr);
}
}
传参问题(要分清是 基本数据类型 还是 引用)
注意,方法传基础数据类型在方法中的改变不会反馈到main函数里面,但是传数组会,因为数组传的是地址
同理,传入一个main中的对象,在方法中对该对象进行属性的改变,也会反馈到main中
public class try{
public static void main(String[] srg){
a xixi = new a();
xixi.test = 1;
int[] xixixi = {1,2};
int xixixixi = 1;
fun hello = new fun();
hello.fun1(xixi);
System.out.println("After change xixi is " + xixi.test);
hello.fun2(xixixi);
System.out.println("After change xixixi is " + xixixi[0]);
hello.fun3(xixixixi);
System.out.println("After change xixixixi is " + xixixixi);
hello.fun4(xixi);
System.out.println(xixi.test);
}
}
class a{
int test;
}
class fun{
public void fun1(a GG){
GG.test = 66;
}
public void fun2(int[] a){
a[0] = 66;
}
public void fun3(int a){
a = 66;
}
//实参原本和main中的xixi同指向一个地址,如果改变具体数值会有影响
// 但如fun4一样指向null不会对main有影响
public void fun4(a GG){
GG = null;
}
}
重载
- 方法名要相同
- 参数类型或个数或顺序至少一个不同
- 返回类型无要求
class fun{
public void cout(int n){
System.out.println(n);
}
public void cout(double n){
System.out.println(n);
}
}
可变参数
传参长度不固定,本质是一个数组
可变参数实参可直接为数组
当可变参数和普通参数一起放在形参列表,要保证可变参数在最末尾
一个形参列表里只能有一个可变参数
public class t1{
public static void main(String[] srg){
Scanner cin = new Scanner(System.in);
int n1 = cin.nextInt();
int n2 = cin.nextInt();
int n3 = cin.nextInt();
fun a = new fun();
System.out.println(a.fun1(n1,n2,n3));
}
}
class fun{
public int fun1(int... num){
int sum = 0;
for(int i = 0;i < num.length;i ++) sum += num[i];
return sum;
}
}
作用域
属性和局部变量可以重名,访问时遵循就近原则
构造器(构造函数)
完成对象的初始化,new的时候自动调用
可以重载以实现不同的传参初始化
public class t1 {
public static void main(String[] srg){
//test、test2都是对象引用
person test = new person(1,"hah");
person test2 = new person();
}
}
class person{
//注意直接对属性的初始化会在构造器前执行
int age = 90;
String name;
//没定义构造方法的时候的默认构造器,如果有构造方法时要重新声明,不然无参会报错
public person(){}
public person(int a,String b){
age = a;
name = b;
}
}
this关键字
一个指向当前对象的指针
在那个对象里就代表哪个对象
class fun{
int a;
public void fun1(int n){
this.a = n;
}
}
this(参数列表)可以访问构造器,只能在构造器中使用
并且要在第一行
class fun{
fun(){
this(1,"hah");
System.out.println("this()要在第一行");
}
fun(int a,String b){}
}
包
命名规范和使用
命名规则一般是com.公司名.项目名.业务模块名
类似文件夹,起到分区的作用
使用不同包的class的时候可以带包名然后使用如 有com.test1 && com.test2.fun1
import com.test1.fun1;
public class test {
public static void main(String[] args) {
fun1 fun1 = new fun1(); //要么导入然后不带包名
com.test2.fun1 fun11 = new com.test2.fun1(); //要么带包名引用
}
}
package
package com.test2; //作用是声明当前类所在的包,放在最上面,一个类最多只有一个package
import java.util.Scanner; //impor在package后,在类定义前,可以多句
public class fun1 {}
访问修饰符
级别 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|
private | √ | × | × | × |
默认 | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
封装
意义是隐藏实现细节、对数据进行验证等
How to
- 属性设为private
- 提供一个public的方法进行set
- 提供一个public的方法进行get
class person{
public String name;
private int age;
private int salary;
person(){}
//构造器也能遵守规则
person(int age,int salary,String name){set(age,salary,name);}
public void set(int age,int salary,String name){
if(age < 0 || age > 120) return;
if(name.length() < 2 || name.length() > 6) return;
this.name = name;
this.age = age;
this.salary = salary;
}
public void show(){
System.out.println(name + " " + age + " " + salary);
}
}
继承
父类 == 基类 == 超类 --------- 子类 == 派生类
父子关系是相对的
子类继承了所有属性和方法、但private类型不能被子类直接访问
提高了复用性、扩展性、维护性
语法 && 实例
class 子类 extends 父类
实例:com.learn.extend_
more detail
- 子类的构造器必须调用父类的构造器,对父类进行初始化,默认调用父类的无参构造器
- 若父类不存在无参构造器,则必须用super指定构造器
- super构造器需要放在构造器的第一行(super只能在构造器中使用)
- 不能同时使用(super() 父类构造器 和 this() 自身构造器),因为二者都必须是第一句
- 所有的类都是object的子类
ctrl + H 可以查看层级关系
- 单继承机制(与cpp区别),一个子类只能有一个直接父类
super
可以通过super实现对父类属性和方法的访问
属性和方法同理,下面详细说方法
如果不重名 super.a() == this.a() == a()
否则只能通过 super.a() 来访问父类方法
对 a() 这种形式的调用,先找本类,有就调用,没有再找父类,再父类的父类直到Object,若找到了但是无法访问,报错,若没找到,提示方法不存在
快捷键 ctrl + B 可以快速访问方法所在的类
super也可以访问更上一级的属性和方法
比如 A -> B -> C
A.a() B.b() C.a()
在C中 super.a() 访问到的是 A.a()
遵循就近原则
不能在子类构造器第一行以外的地方调用super()构造器
实例com.learn.super_
super和this的比较
区别 | this | super |
---|---|---|
访问属性 | 先找本类,没有再找父类 | 直接找父类 |
访问方法 | 先找本类,没有再找父类 | 直接找父类 |
调用构造器 | 本类构造器,必须放在首行 | 父类构造器,必须放在子类构造器首行 |
特殊 | 表示当前对象 | 子类中访问父类对象 |
方法的重写/覆盖(override)
子类的方法和父类一样(名称、参数),子类返回类型一样或者返回类型是父类返回类型的子类,子类的方法会覆盖父类的方法
子类方法不能缩小父类方法的权限
如 : protected -> private ×
但 : protected -> public √
实例:com.learn.override_
多态
前提是存在继承关系
重载和重写就体现了多态、
相当与用父类接受子类并当子类使用
当Dog和Cat是Animal的子类的时候
public void feed(Animal animal){} 可以传入Dog类或者Cat类正常使用
对象的多态
- 一个对象的编译类型和运行类型可以不一样
- 编译类型在编译的时候就确定了,无法改变
- 运行类型是可以变化的
- 编译类型看等号的左边,运行类型看等号的右边
Animal animal = new Dog(); //编译类型 Animal 运行类型 Dog
animal = new Cat(); //编译类型 Animal 运行类型 Cat
向上转型
语法 : 父类类型 引用名 = new 子类类型
A -> B -> C;
A a = new C();
B b = new C();
父类编译类型,子类运行类型
父类指父亲、爷爷、曾爷爷...
不能调用子类的特有成员
在编译阶段还是属于父类
但是在运行的时候调用的就是子类成员了
编译阶段由编译类型决定,运行阶段由运行类型决定
向下转型
语法 : 子类类型 引用名 = (子类类型) 父类引用
Animal -> Cat
要求父类引用必须指向强转对象
Animal animal = new Animal();
animal = new Dog();
Dog dog1 = (Dog) animal;
animal = new Cat();
//Dog dog1 = (Dog) animal; //animal当前指向Cat,不能转成Dog
Cat cat1 = (Cat) animal; //转换过后此时的animal & cat1 都是 Cat 的引用
属性重写问题
属性没有重写一说,值和编译类型有关
下面输出为 1
package com.learn.PolyMethod_;
public class AboutNumber {
public static void main(String[] args) {
A a = new B();
System.out.println(a.number);
}
}
class A{
int number = 1;
}
class B extends A{
int number = 2;
}
动态绑定机制 (重要)Dynamic_binding
调用方法的时候,方法会和对象的内存地址/运行类型绑定
调用属性的时候,没有动态绑定机制,那里声明哪里使用
多态数组
数组的定义类型为父类类型,保存的实际元素为子类类型
Objec类详解
1. equals
equals 和 == 的区别
对引用来说 -> == 判断的是地址
对基础数据类型 -> == 判断内容,也可以看作判断地址,相同常量在常量池中位置相同
基础数据类型是存放在常量池里的,如 int a = 1,b = 1,a == b
对不是基础数据类型的数据来说,内容相同不一定地址相同
为什么是判断内容
默认是判断地址是否相等,但是子类往往会对其进行重写,使其进行内容判断
不确定的话可以ctrl + b查看源码
在实际使用中可以自己重写 com.learn.equals_
如 String、Integer
2. hashCode
根据地址得来的hash编码,是一个int
3.toString
默认返回:全类名 + @ + 哈希值的十六进制
如 :com.learn.toString_.Monster@776ec8df
子类往往重写toString方法来返回对象的属性信息(快捷键重写)
打印或拼接对象的时候调用
直接输出的时候默认调用 toString 方法 -> sout(Monster) == sout(Monster a.toString())
4.finalize
该对象的生命周期结束(比如赋值n ull),在销毁对象前自动调用 (析构函数?
该方法已经被弃用了...
断点调试(DEBUG)
属于运行状态,可以看作正常运行,判断依据是运行类型
快捷键
- F7 跳入
- F8 跳过 / 逐行执行
- shift + F8 跳出
- F9 resume 执行到下一个断点
static关键字
声明为静态
对象方法声明为静态可以通过 类名.方法() 直接调用
如 People.say();
在类中声明一个 静态变量 ( 类变量 )
这个变量是这个类的所有对象共享的
class A{
public static int count = 0;
}
A a1 = new A();
A a2 = new A();
a1.count ++,a2.count ++:
//count == 2
类变量可以通过类名访问,如
A.count ++;
java设计者推荐访问方式
- 当然,与类变量相似的,也可以定义类方法。
静态方法的好处是在使用的时候可以直接通过类名调用,而不用建立一个实例化对象
这样的话在一些工具类里声明静态方法,用起来就比较方便
注意
-
类方法中没有 this & super 具体原理参考 jvm 八股文
-
静态方法只能访问静态变量或者静态方法,而普通方法的访问没有限制
main方法详解
main方法是 JVM 调用的
并且 JVM 调用时不同包、也不创建对象,所以修饰符必须是 public + static
String[] args 有什么作用
用于接受文件名后的字符,看作一个字符串数组
传入的数据是执行程序时 执行的程序 后面的字符串
java 执行的程序 参数1 参数2 参数3
main 是该类中的 static 成员,可以调用静态成员和参数,遵循 static相关的规则
代码块
[修饰符]{ 代码 };
修饰符一般只有 static 可选
又称为初始化块,属于类中的成员、是类的一部分,类似于方法,将逻辑语句封装在方法体中,通过{} 包围起来。
与类方法不同的是,没有方法名、没有返回、没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用
看作构造器的补充
class Movie{
public String name;
public double price;
public Movie(String name) {
this.name = name;
System.out.println("Only name");
}
public Movie(String name, double price) {
this.name = name;
this.price = price;
}
//代码块的优先级永远高于构造器,改代码运行会先运行代码块
{
System.out.println("这是代码块");
System.out.println("不管调用哪个构造器都会调用这个代码块");
}
}
细节
-
static代码块也叫做静态代码块,作用就是对类进行初始化
不管 new 多少新对象,该代码块只会在第一次加载类的时候运行
static {
System.out.println("这是代码块");
System.out.println("不管调用哪个构造器都会调用这个代码块");
}
-
什么时候类会被加载
- 创建对象实例的时候 ( new )
- 创建子类对象实例,父类也会被加载
- 使用该类的静态成员的时候 ( 包括属性和方法 )
-
普通代码块在创建对象实例的时候会被调用,创建一次就调用一次,如果使用该类的静态成员,普通代码块与静态代码块不同,不会执行
-
创建一个对象时的调用顺序
-
static 代码块和属性 并按定义顺序
-
普通代码块和属性 并按定义顺序
-
构造方法
-
-
构造器隐含的语句,而static型此时早已初始化完毕
class A{
public A{
//super();
//代码块;
---------------
构造器内容
}
}
-
继承时的顺序
- 父类 static
- 子类 static
- 父类普通
- 父类构造
- 子类普通
- 子类构造
-
静态代码块只能直接调用静态成员,普通代码块可以调用所有
单例设计模式
饿汉式(SingleTon01_)
目的:使一个类只创建一个对象
类加载的时候就被创建,和是否使用没有关系(static 关键字作用)
- 将构造器私有化
- 类的内部直接创建
- 提供一个公共的static方法用于返回在类的内部直接创建的对象
线程安全但是可能存在浪费资源的问题
懒汉式(SingleTon02_)
防止饿汉式创建不使用造成空间浪费
- 构造器私有化
- 定义一个 static 对象
- 提供一个 public 方法返回对象
不会造成资源浪费
但是存在线程安全问题
final关键字 ( 有点像 const ? )
java 中没有 const 通过 static final 来实现常量的定义
什么时候会用到final关键字
- 当不希望类被继承的时候,可以用 final 修饰
- 当不希望父类的方法被子类改变时
- 当不希望类的某个属性可以被修改
- 当不希望某个局部变量被修改
注意事项和使用细节
- final 修饰的属性又叫常量,一般用XX_XX_XX_XX 命名,如:TAX_RATE
- final 修饰的属性在定义的时候必须赋初值,确定后不能再修改
- 定义时直接等号赋值
- 构造器中赋值
- 代码块中赋值
- 如果是 static final 初始化位置只能是
- 定义时直接等号赋值
- 静态代码块中赋值
- final 类不能继承,但可以实例化对象
- 一个类不是 final 类但是含有 final 方法,该方法不能重写但是可以被继承
- 一般来说,一个类如果是 final 的,就没必要再对方法进行 final 修饰
- static final 搭配使用的时候不会导致类加载,底层编译器进行了优化
- final 不能修饰构造器
- 包装类 (Integer , Double , Float , Boolean ) 都是 final,String 也是
抽象类
当父类方法不确定的时候,考虑将其设为抽象类
抽象方法就是没有实现的方法,即没有方法体
如果定义抽象方法,则该类必须为抽象类
一般来说,抽象类会被继承,由其子类来实现抽象方法
如:public abstract void eat(); //没有方法体
细节
- 抽象类不能被实例化
- 抽象类不一定要包含抽象方法,但包含抽象方法一定得是抽象类
- abstract 只能修饰类和方法,不能修饰属性和其他的
- 如果一个类继承了抽象类,则它必须实现抽象类的抽象方法、除非自己也声明为抽象类
- 抽象方法不能用 final、static、private 修饰,这些关键字都是和重写相违背的,共用会报错
抽象模板模式 P401
接口
接口就是给出一些没有实现的方法,封装到一起,到某个类要用的时候再具体实现
定义 :interface B{}
使用方式 :class A implements B {}
A 实现 B 接口
B 是一个接口,A类中必须实现B中定义的方法
JDK 7.0 前的接口没有方法体,8.0 以后可以定义默认方法(default修饰)、静态方法
在接口中,抽象方法可以省略 abstract 形容词
细节
- 接口不能实例化
- 接口中所有的方法是 public ,接口中的抽象方法可以不用 abstract 修饰
- 一个普通类实现接口,就必须将该接口的所有方法实现
- 抽象类实现方法可以不用实现接口的方法
- 一个类可以实现多个接口 -> class A implements B,C
- 接口中所有的属性是 final ,对 int a 实际上是 public static final int a; 所以可以 接口名.属性名
- 接口不能继承类,但可以继承其他的接口
- 接口的修饰符只能是 public 和 默认,这点和类的修饰符是一样的
和继承的区别
接口主要是把接口的功能在当前类上实现
继承主要是把父类的功能在当前类上实现
继承主要是解决代码的可维护性和复用性
接口主要是解决设计规范问题并且接口更为灵活
接口的多态特性
com.learn.Interface_
接口类型的变量可以指向实现了该接口的对象实例
与继承的多态相似
父类类型的变量可以指向子类的对象实例
与继承的多态一样,实现特殊的方法需要向下转型后调用
多态传递
interface A{}
interface B extends A{}
class C implements B{}
//相当与爷爷指向孙子,多态传递
A a = new C();
内部类
一个类(A)的内部又完整的嵌套了另一个类(B),A 就叫外部类,B就叫内部类
内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
类的五大成员
属性、方法、代码块、构造器、内部类
分类
定义在外部类的局部位置上(一般在方法内)
1. 局部内部类(有类名)
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,它的地位就是一个局部变量,只能用 final 修饰
- 作用域:定义它的方法、代码块中
- 外部类调用内部类 -> new 然后调用
- 它本质还是一个类、但地位是一个局部变量,只能在定义域内使用
- 与外部类的成员重名时要调用外部类成员 使用 :外部类名.this.成员(如:Outer.this.same)
2. 匿名内部类(没有类名、重点) Anonymous
- 该类没有名字(底层分配有名字,但表面是没有的)、同时它还是一个对象
- 具体看 com.learn.InnnerClass_.AnonymousInnerClass_
- 格式: new 类或接口(){}
- 可以不接收直接调用
定义在外部类的成员位置上
1. 成员内部类
- 定义在外部类的成员位置、可直接访问外部类的所有成员
- 可以添加任意访问修饰符,因为它的地位就是一个成员
- 外部类调用内部类 -> new 然后调用
- 重名调用外部 name && Outer.name
2. 静态内部类(static)
- 只能访问静态成员,不能访问非静态成员
- 外部类调用内部类 -> new 然后调用
- 重名调用外部 name && Outer.name
异常 ( Exception )
快捷键 crtl + alt + t
e.getMessage() //得到简单错误信息
Error 和 异常 不同,是虚拟机无法处理的严重问题,如栈溢出、Out of memory等,程序会崩溃
异常分为 运行时异常 和 编译时异常
运行时异常
- 空指针使用异常 (Test01)
- 数学运算异常 (Test02)
- 数组越界 (Test03)
- 类型转换异常 (Test04)
- 数字格式不正确异常 (Test05)
编译时异常
对于编译异常,程序中必须处理,try - catch 或者 finally
异常处理
1) try - catch - finally
程序员自己处理
异常发生时,系统会把捕获到的异常封装成一个 EXception 对象 传入 catch 的 Exception e 中
得到 Exception 后自行处理,没有异常则不会执行 catch
发生异常后会跳入 catch 不会执行剩下的代码
finally 始终会被执行,通常放入释放资源的代码
细节
- 可以有多个 catch ,必须是子类异常在前,只会进入一个 catch 语句
- 也可以 try - finally ,但这样相当于没有捕获异常,出现异常会造成程序崩溃
2) throws
将异常抛出,交给调用者处理,最顶级的处理者就是 JVM ( 不断抛给上一个调用者 )
如 JVM -> main -> f1 -> f2(抛出异常)
则 f2 -> f1 -> main -> JVM
//除 JVM 的可以选择 try - catch -finally
//JVM 是直接输出异常信息,然后终止程序
细节
- 对于程序中出现的异常,没有特殊处理默认就是throws
- 在 throws 过程中如果有 try - catch 处理就不用继续 throws
- 子类重写父类的方法时,子类重写的方法抛出异常类型要么和父类一样,要么是子类
自定义异常
定义一个类,继承 Exception (编译异常)或者 RuntimeException(运行异常)
com.learn.Exception_.CustomException_
throw 和 throws 的区别
意义 | 位置 | 后面跟的 | |
---|---|---|---|
throws | 异常处理的一种方式 | 方法声明处 | 异常类型 |
throw | 手动生成异常对象的关键字 | 方法体中 | 异常对象 |
枚举(enumeration)
枚举是一组常量的组合,它属于一种特殊的类,里面只包含一组有限的特定对象
实现方式
自定义类
使用 enum 关键字
com.learn.enum_
注意事项
- 当我们使用 enum 关键字创建一个枚举类的时候,默认继承 Enum 类,并且是一个 final 类
所以可以使用 Enum类 的方法
- 使用无参构造器创建枚举对象的时候,小括号可以省略 如 ( SPRING; )
- 创建多个枚举对象的时候使用 , 间隔,并在结尾加上 ;
- 枚举对象必须放在枚举类的行首
Enum类的方法
- toString :已经重写过了,返回的是当前对象名 ,子类可以重写
- name : 返回当前对象名,子类不能重写
- ordinal :返回当前对象的位置号,默认从 0 开始
- values :返回当前枚举类中所有的常量 (返回的是一个数组,包含所有的枚举对象)
- valueOf :将字符串转化成枚举对象,要求字符串必须为已有的常量名,否则报异常
- compareTo :比较两个枚举常量,比较的就是位置号
细节
- enum 隐式继承了 Enum 类了,不能再继承其他类
- 枚举类可以使用接口
注解
注解也被成为元数据,用于修饰数据星信息
和注释一样,注解也不影响程序逻辑,但注解可以被编译和运行,相当于嵌入在代码中的补充信息
三个基本注解
-
@Override :限定某个方法,是重写父类方法,该注解只能用于方法
-
@Deprecated :用于表示某个程序(类、方法等)已过时
过时并不代表不能用而是不推荐使用,还是可以正常使用的
-
@SuppressWarnings :抑制编译器警告
用法 @SuppressWarnings("参数") 参数有很多,可以抑制不同类型的警告,也可以填入 all 全部抑制
形参是一个 String 数组,所以要注意传入规范
-
@interface :注解类型 (比如 int 是整形)
四个元注解 (P435)
- @Target :修饰注解的注解,称为元注解
- @Retention :指定注解的作用范围(SOURCE 源码,CLASS 类,RUNTIME 运行时)
- @Documented :指定该注解是否会在javadoc体现
- @Inherited :子类会继承父类注解