本文已参与「新人创作礼」活动,一起开启掘金创作之路。
JAVA基础语法
java基础01:注释
养成书写注释的习惯
-
单行注释:注释一行
//
-
多行注释
/*
注释内容
*/
-
文档注释(javadoc)
/**
*@Description hello world
*@Author jack
*/
-
多行注释不可嵌套多行注释和文档注释,可以嵌套单行注释
java基础02:标识符和关键字
-
关键字
-
Java所有的组成部分都需要名字。类名,变量名以及方法名都称为标识符
标识符注意点
- 所有标识符都是应该是以字母(A-Z或a-z),美元符$,或者下划线_开始
- 首字母之后可以是字母(A-Z或a-z),美元符$,或者下划线_或数字的任何字符组合
- 不能用关键字作为变量名或方法名
- 标识符是大小写敏感的
- 合法标识符:age、$salary、_value、__1_value
- 非法标识符:1223abc、-a、#abc
- 不建议中文命名
基础语法
- 大小写敏感:Java是大小写敏感的语言
- 类名:首字母大写,驼峰命名,例如MyFirstClass
- 包名:小写,例如 my.first.package
- 方法名:首字母小写,驼峰命名,例如 myMethod()
Java基础03:数据类型
基本数据类型
- 在Java中,基本数据类型只有四类八种
-
整数型:
数据类型 字节数 位数 默认值 范围 byte 1 8 0 -128~127 short 2 16 0 -32768~32767 int 4 32 0 -2147483648~2147483647 long 8 64 0L -
浮点型:
数据类型 字节数 位数 默认值 float 4 32 0.0f double 8 64 0.0d -
字符型
char,char类型是一个单一的16位Unicode字符,占两字节,最小值为\u0000(也就是0),最大值为\uffff(也就是65535),char类型可以存储任何字符。
char a = 'A'; -
布尔型
boolean,boolean只有两种值,true或false,只占1位,默认值是false
引用数据类型
-
Java中引用数据类型类似于C/C++的指针,引用类型指向一个对象,指向对象的变量就是引用变量。这些变量在声明时被指定为一个特定的类型,例如Employee、Puppy等,变量一旦声明不能改变
-
对象、数组都是引用数据类型
-
所有引用数据类型的默认值都是null
-
一个引用变量可以用来引用任何与之兼容的类型
-
例如:
Site site = new Site("Runoob");
数据类型扩展
- 整数:二进制:0b 八进制:0开头 十六进制:0x
- 浮点数:不要用浮点数进行比较
//float 表示有限的 离散的 舍入误差 大约 接近但不等
//double
float f = 0.1f; //0.1
double d = 1.0/10; //0.1
System.out.println(f==d); //false
float d1 = 23333333333333f;
float d2 = d1 + 1;
System.out.println(d1==d2);//true
- 字符型:所有字符本质上都是数字
//u0000 ~ uffff
char c = '\u0061';
System.out.println(c);//a
转义字符
| 符号 | 字符含义 |
|---|---|
| \n | 换行(0x0a) |
| \r | 回车(0x0d) |
| \f | 换页符(0x0c) |
| \b | 退格(0x08) |
| \0 | 空字符(0x0) |
| \s | 空格(0x20) |
| \t | 制表符 |
| \ " | 双引号 |
| \ ' | 单引号 |
| \ \ | 反斜杠 |
| \ddd | 八进制字符(xxx) |
| \uxxxx | 16进制Unicode字符(xxxx) |
Java基础04:类型转换
- 由于Java是强类型语言,所以进行运算时有时需要用到类型转换
低 ----------------------------------------------> 高
byte short char -> int -> long -> float ->double
-
从低到高:自动类型转换
-
从高到低:强制类型转换,注意内存溢出
注意点
- 布尔值不能转换
- 高容量到低容量强制转换
- 转换过程可能存在内存溢出或精度问题
Java基础05:变量、常量、作用域
变量
- 变量即可变化的量
- Java是一种强类型的语言,每个变量都必须声明其类型
- Java变量是程序中最基本的存储单元,其要素有变量名、变量类型和作用域
注意事项
- 变量的类型可以是基本类型也可以是引用类型
- 变量名必须是合法的标识符
- 变量声明是一条完整的语句以分号结束
变量作用域
- 类变量(静态变量):static修饰,加载类文件到方法区时在堆中单独开辟静态变量的空间单独存储静态变量
- 实例变量:独立于方法之外的变量,不过没有static修饰
- 局部变量:类的方法中的变量
public class Temp {
static int a = 0;//类变量
String str = "hello";//实例变量
public void run() {
int i = 0;//局部变量
}
}
常量
- 常量(constant):初始化后不能再改变的值
final 常量名 = 值;
final double PI = 3.14;
- 常量名一般使用大写字母
Java基础06:基本运算符
算数运算符:+,-,*,/,%,++,--
赋值运算符:=
关系运算符:>,<,>=,<=,==,!=,instanceof
逻辑运算符:&&,||,!
位运算符:&,|,^,~, >>,<<, >>>
条件运算符:? :
扩展赋值运算符:+=,-=,*=,/
- 逻辑运算符 与、或、非
| 运算符 | 作用 |
|---|---|
| && | 短路与 |
| || | 短路或 |
| ! | 逻辑非 |
| & | 逻辑与 |
| | | 逻辑或 |
| 逻辑异或 |
- 按位运算符
按位运算符用来操作整数基本类型中的每个比特位,也就是二进制位。按位操作符会对两个参数中对应的位执行布尔代数运算,并最终生成一个结果。
| 运算符 | 作用 |
|---|---|
| & | 与 4 & 5 = 4 |
| | | 或 4 | 5 = 5 |
| ~ | 非 ~4 = -5 |
| 异或 4 ^ 5 = 1 |
Java流程控制
Scanner类
java.util.Scanner是Java5的新特征,通过Scanner类来获取用户的输入
//创建扫描器对象,用于接收键盘数据
Scanner scanner = new Scanner(System.in);
//判断用户是否输入
if (scanner.hasNext()) {
//使用next方法接收
//next()遇到空白直接返回,无法得到空格后的输入
String str = scanner.next();
System.out.println(str);//
}
//凡属于IO流的类不关闭会一直占用资源,养成良好习惯及时关闭
scanner.close();
//使用nextLine()方法
//返回的是输入回车前的所有字符,包括空格
String str1 = scanner.nextLine();
//限制输入
//整型数据
int a = scanner.nextInt();
//浮点型数据
float f = scanner.nextFloat();
//若输入数据与限制类型不匹配则抛出InputMismatchException输入不匹配异常
条件语句
条件语句可根据不同的条件执行不同的语句。包括if条件语句和switch多分支语句
if条件语句
if语句可以单独判断表达式的结果,表示表达的执行结果,例如:
int a = 10;
if (a > 10) {
return true;
}
return false;
if...else 条件语句
if语句还可以与else连用,表现为**如果满足某种条件,就进行某种处理,否则进行另一种处理。
int a = 10;
int b = 12;
if (a > b) {
return a;
} else {
return b;
}
if后的()内的表达式必须是boolean型的,如果为true,则执行if后的复合语句;如果为false,则执行else后的符合语句。
if...else if多分支语句
上面的if...else是单分支和两个分支的判断,如果有多个判断条件,就需要使用if...else if
int x = 90;
if (x < 60) {
System.out.println("x小于60");
} else if (x < 80) {
System.out.println("x大于60且小于80");
} else if (x < 90) {
System.out.println("x大于80且小于90");
} else {
System.out.println("x大于等于90");
}
switch多分枝语句
一种比if...else if语句更优雅的方式是使用switch多分支语句
Scanner scan = new Scanner(System.in);
String week = scan.nextLine();
switch (week) {
case 1:
System.out.println("Monday");
break;
case 2:
System.out.println("Tuesday");
break;
case 3:
System.out.println("Wednesday");
break;
default:
System.out.println("No Else");
break;
}
循环语句
循环语句就是满足一定条件下反复执行某一表达式的操作,直到不满足该条件
while循环语句
while循环语句的循环方式位利用一个条件来控制是否要反复执行该语句
while(布尔值) {
//表达式
}
当布尔值位true时执行表达式,当布尔值位false时结束循环,布尔值也可是一个表达式
int a = 10;
while (a > 8) {
a--;
}
do...while循环
do...while与while唯一的区别是do...while语句至少执行一次
int b = 10;
do {
System.out.println("b=" + b);
b--;
} while (b==1);
for循环语句
for循环是经常使用的循环方式,这种形式会在第一次迭代前进行初始化。
for (初始化; 布尔表达式; 步进) {
//循环体
}
每次迭代前会测试布尔表达式。如果获得的结果是false就结束循环;每次循环按照步进的值执行下一次循环。
- 逗号操作符
Java里唯一用到逗号操作符的就是for循环控制语句,在表达式的初始化部分和步进部分可以使用逗号分隔的语句,在for语句中定义多个变量,但他们必须有相同的类型
for (int i = 1, j = i + 1; i < 6; i++, j = j * 2) {
}
for-each语句
在Java JDK 1.5中引入了一种更加简洁的、方便的对数组和集合进行遍历的方法,即for-each语句
int array[] = {1,2,3};
for (int arr : array) {
System.out.println(arr);
}
跳转语句
Java语言中有三种跳转语句:break、continue和return
break
break用于终止循环
for(int i = 0; i < 10; i++) {
if (i == 5) {
break;
}
}
continue语句
continue在循环语句中与break有相反的效果,它的作用是跳过本次循环,直接执行下一次循环
for(int i = 0; i < 10; i++) {
if (i == 5) {
System.out.print("continue");
continue;
}
System.out.print(i);
}
/*
结果:1234continue6789
*/
return语句
return语句可以从一个方法返回,并把控制权交给调用它的语句
public void getName() {
retrun name;
}
Java方法
方法01
-
System.out.println()
system是一个类,out是system的一个对象,println即方法
调用系统类中的标准输出对象out中的println方法
-
java方法是语句的集合,在一起执行一个功能。
包含于类或对象中
方法在程序中被创建,在其他地方被引用
-
设计方法的原则:方法本意是功能块,实现某个功能的语句块的集合。设计方法的时候最好保持方法的原子性,即一个方法只实现一个功能,有利于后期扩展。
-
方法命名规则:首字母小写,后面驼峰命名
方法02:定义和调用
- Java方法包含一个方法头和一个方法体:
- 修饰符:定义了该方法的访问类型
- 返回值类型:方法可能会有返回值,无返回值时为void
- 方法名:是方法的实际名称,方法名和参数列表共同构成方法签名
- 参数类型:当方法调用时,传递值给参数,这个值被称为实参或变量。参数列表指参数的类型,顺序,个数。方法可以不含参数
- 方法体:方法体包含具体的语句,定义该方法的功能。
修饰符 返回值类型 方法名(参数列表 参数名) {
方法体
return 返回值;
}
方法03:方法重载
在Java中方法的重载,是类名的不同表现形式,构造函数也是重载的一种。另一种就是方法的重载
- 方法重载就是在一类中,有相同的函数名称,但形参不同的函数
- 方法重载的规则:
- 方法名必须相同
- 参数列表必须不同(参数个数、顺序、类型不同)
- 仅仅返回类型不同不足以构成方法的重载
- 重载发生在编译时,因为编译器可根据参数类型选择使用哪个方法
public class Apple {
int sum;
String color;
//构造方法
public Apple(){};
public Apple(int sum) {
this.sum = sum;
}
//getApple重载
public int getApple(int num){
return num;
}
public String getApple(String color) {
return color;
}
}
方法重写
方法重写与方法重载是完全不同的东西,方法重写描述的是子类和父类之间的方法,而重载指的是同一类中的方法
class Fruit {
public void eat() {
System.out.println("eat fruit");
}
}
class Apple extends Fruit {
@Override
public void eat() {
System.out.println("eat apple");
}
}
方法04:方法命令行传参
- 运行一个程序时传递给它参数。要依靠命令行参数给main()函数实现
public static void main(Strng[] args) {
//args.length 返回数组长度
for (int i = 0; i < args.length; i++) {
System.out.println("args[" + i + "]: " + args[i]);
}
}
方法05:可变参数(不定向参数)
- JDK1.5开始,Java支持传递同类型的可变参数给一个方法。
- 在方法的声明中,在指定参数类型后加一个省略号(...)
- 一个方法只能指定一个可变参数,且必须是方法的最后一个参数,其他参数必须在他之前声明
public static void printMax(double...x) {
//排序
double result = x[0];
for (int i = 1; i < x.length; i++) {
if (x[i] > result) {
result = x[i];
}
}
System.out.println("Max is" + result);
}
方法06:递归
-
递归即方法自己调用自己
-
递归结构包括两个部分:
- 递归头:定义递归的结束条件
- 递归体:何时调用自身方法
java使用的是栈机制,每调用一个方法压入栈中,栈底为main方法
//1! = 1
//2! = 2*1
//3! = 3*2*1
public static int fun(int n) {
if (n == 1) {//结束条件
return 1;
} else {
return n * fun(n-1);//递归体
}
}
数组
- 数组是相同数据类型的有序集合
- 数组描述的是相同类型的若干个数据,按照一定的先后顺序排列组合而成
- 每一个数据称作数据元素,通过下标访问
数组声明创建
- 必须声明数组变量,才能在程序中使用数组
dataType[] array;//首选方式
或
dataTyep array[];//不推荐使用,效果相同
- Java语言使用new操作符来创建数组
dataType[] array = new dataType[arraySize];
- 数组的元素是通过索引访问的,数组索引从0开始
- 获取数组长度:arrayName.length
内存分析
- 示例:
int[] a = new int[10];//声明数组并在堆中分配内存空间
System.out.println(a.length);//10
//数组静态初始化:创建同时赋值
int[] b = {1,2,3,4};
//动态初始化:默认初始化为该类型的默认值
int[] c = new int[4]; //默认值都为0
小结
- 数组长度固定,一旦被创建不可改变
- 其元素必须是相同类型,不允许出现混合类型
- 数组元素可以是任何类型,包括基本类型和引用类型
- 数组变量属于引用类型,数组也可以看成是对象,数组中每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象在堆中,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中
Arrays类
- 数组的工具类java.util.Arrays
- Java提供了一个工具类Arrays,从而可以对数组中的数据对象进行一些基本操作,查询API文档可知
- Arrays类中的方法都是static修饰的静态方法,可直接使用类名进行调用
常用方法
- 给数组赋值:fill方法
int a = new int[10];
//将指定值1分配给a数组的每个元素
Arrays.fill(a,1);
//将指定值5分配给a数组中指定范围内的元素
Arrays.fill(ast,0,9,5);
- 对数组进行排序:sort方法,升序排序
int[] b = {23,43,2,32,12,14,6};
//排序
Arrays.sort(b);
for(int t : b) {
System.out.print(t + " ");
}
//结果:2 6 12 14 23 32 43
- 比较数组:equals方法比较两个数组中元素是否相等
int[] a = {1,2,3};
int[] b = {1,2,3};
int[] c = {};
System.out.println(Arrays.equals(a,b));//true
System.out.println(Arrays.equals(a,c));//false
- 查找元素:通过binarySearch方法对排序好的数组进行二分查找,返回被查找元素的下标,查找失败则为二叉判定树失败节点的负值
int[] b = {1,2,3,4,6,8};
System.out.println(Arrays.binarySearch(b,3));//2
面向对象
面向对象01:什么是面向对象
- 面向对象编程(Object-Oriented Programming,OOP)
- 面向对象的本质:以类的方式组织代码,以对象的组织(封装)数据
- 面向对象具有抽象、封装、继承、多态等特性
- 认识角度:先有对象后有类。对象是具体的事物。类是抽象的,是对对象的抽象
- 代码运行角度:先有类后有对象,类是对象的模板
- 将复杂的业务逻辑简单化,增强代码复用性
面向对象02:回顾方法的定义和调用
public class Demo01 {
//main方法
public static void main(String[] args) {
}
//static修饰的方法与类一同加载
public static void a() {
b();//报错,非静态方法在类实例化后才可经对象调用
}
private void b(){}
}
public class Demo02 {
//引用传递
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);//null
Demo02.change(person);
System.out.println(person.name);//sss
}
public static void change(Person person) {
//person是一个指向类的对象
person.name = "sss";
}
static class Person {
String name;
}
}
面向对象03:类与对象的创建
在Java中万事万物都是对象。尽管一切都是对象,但是可操纵都是一个对象的引用。有一个对象引用,但不一定需要一个对象与之关联。
Car carKey;//一个对象引用
这里创建的只是引用,而并非对象,但是如果你想要使用carKey这个引用时,会返回一个异常,告诉你需要一个对象来和这个引用进行关联。一种安全的做法是,在创建对象引用时同时把一个对象赋给它。
Car carKey = new Car();
- 使用new关键字创建对象
- 使用new关键字创建的同时,除了分配内存空间外,还会给创建好的对象进行默认的初始化即对类中构造器的调用。
- 类中的构造器也称构造方法,是在创建对象时必须调用的,具有以下特点:
- 方法名与类名相同
- 无返回类型,也不能写void类型
IDEA :alt+insert 快捷创建构造方法
IDEA : 在 Project Structure 中添加out目录查看class文件
构造方法
- 在Java中的一种特殊的方法,也被称为构造函数、构造器等。在Java中通过提供这个构造器来确保每个对象都被初始化。构造方法只能在对象的创建时期调用一次。
public class Person {
//一个类什么都不写也会存在默认构造方法
String name;
//无参构造
public Person() {
this.name = "null";
}
//有参构造
public Person(String name) {
this.name = name;
}
//new实例化一个对象,本质调用构造方法
Person person = new Person("zz");
}
初始化顺序
- 静态属性:static开头定义的属性
- 静态方法快:static {} 包含的代码块
- 普通属性:非static定义的属性
- 普通方法快:{} 包起来的代码块
- 构造函数:类名相同的方法
- 方法:普通方法
public class LifeCycle {
//静态属性
private static String staticField = getStaticField();
//静态方法快
static {
System.out.println(staticField);
System.out.println("静态方法块初始化");
}
//普通属性
private String field = getField();
//普通方法快
{
System.out.println(field);
}
//构造函数
public LifeCycle() {
System.out.println("构造函数初始化");
}
//静态方法
public static String getField() {
String field = "Field Initial";
return field;
}
//主函数
public static void main(String[] args) {
new LifeCycle();
}
}
面向对象04:创建对象内存分析
- 堆中存放具体创建的对象
- 栈中存放方法和应用变量名
- 方法区加载类的模板
面向对象05:小结
-
类是一个模板,一个抽象。对象是一个具体的实例
-
方法:定义和调用
-
除八大基本类型外都是引用类型,对象是通过引用来操作的:栈--->堆
-
属性:字段(Field) 成员变量
默认初始化:
数字:0 0.0
char:u0000
boolean:false
引用:null
修饰符 属性类型 属性名 = 属性值
-
对象的创建和使用
- 必须使用new关键字创建对象,同时调用构造器
- 对象的属性
- 对象的方法
-
类包含:
静态的属性
动态的方法
面向对象06 :封装
-
封装又称为访问控制权限,它是面向对象三大特性中的一种
-
访问控制权限的核心:只对需要的类可见
-
类中私有属性通过get/set方法操作
-
Java中成员的访问权限共有四种,分别是**private、protected、public、default:
| 可见性 | private | default | protected | public |
|---|---|---|---|---|
| 同一类 | √ | √ | √ | √ |
| 同一包中的类 | √ | √ | √ | |
| 子类 | √ | √ | ||
| 其他包中的类 | √ |
alt + insert 自动生成get/set方法
隐藏类的属性,通过操作接口来访问
- 提高程序安全性
- 隐藏代码的实现细节
- 统一接口
- 增加可维护性
面向对象07:什么是继承
-
继承是所有面向对象语言不可或缺的部分。在Java中只要我们创建了一个类,就隐式的继承Object父类
-
Java只有单继承,没有多继承。一个子类只能有一个直接父类,一个父类可以有多个子类。但存在间接继承。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zd9m1C4t-1656386563746)(C:\Users\16339\AppData\Roaming\Typora\typora-user-images\image-20220628094402266.png)]
- 继承的关键字是extends,如上图,如果使用了extends显示指定了继承,Father即为父类,Son即是子类
class Father{}
class Son extends Father{}
继承双方拥有某种共性的特征
class Father {
public void feature() {
System.out.println("父类的特征");
}
}
Class Son extends Father {
//若Son没有重写feature方法,则默认用的是父类的feature方法
}
面向对象08:Super详解
- super访问父类的属性和方法
public class Student extends Person {
private String name = "zhang";
public Student() {
//隐藏的代码:调用父类的构造方法:super()
//调用父类构造方法必须写在子类构造方法的第一行
//this.()调用本类的构造,不可与super()同时调用构造方法
super();
System.out.println("子类构造");
}
}
-
创建对象时先调用父类构造器,默认调用无参构造器
-
super调用父类的构造方法,必须在构造方法的第一个
-
super必须只能出现在子类的方法或构造方法中
-
super和this不能同时调用构造方法
-
this()调用本类的构造
-
super()调用父类的构造
面向对象09:方法的重写
重写需要有继承关系,子类重写父类的方法
- 方法名相同
- 参数列表必须相同
- 修饰符:范围可以扩大但不能缩小:public>protected>default>private
- 抛出的异常:范围可以被缩小,但不能扩大:ClassNotFoundException-->Exception(错误)
- 重写,子类的方法和父类必须要一致,方法体不同
public class B {
public void test() {
System.out.println("B->test()");
}
}
public class A extends B {
public void test() {
System.out.println("A->test()");
}
}
public class Test {
//只可重写非静态方法
public static void main(String[] args) {
//方法的调用只与左边定义的数据类型有关
//父类引用指向子类
B a = new A();
a.test(); //A->test()
}
}
面向对象10:什么是多态
-
多态是方法的多态,属性没有多态
-
实现多态的三个充要条件:
-
继承
-
重写父类方法
-
父类引用指向子类
-
father s1 = new Son();
-
-
不可重写的方法:
-
static静态方法不属于实例对象,属于类,不可被重写
-
final 修饰的方法
-
private方法
-
public class Fruit {
int num;
public void eat() {
System.out.println("eat fruit");
}
}
public class Apple extends Fruit {
@Override
public void eat() {
super.num = 10;
System.out.println("eat" + num + "Apple");
}
public static void main(String[] args) {
Fruit fruit = new Apple();
fruit.eat();//调用子类方法
}
}
- mian方法中,Fruit类型的对象指向了Apple对象的引用,这其实就是多态-> 父类引用指向子类对象,因为Apple继承于Fruit,并且重写了eat方法,所以能够表现出来多种状态的形式。
组合
- 组合就是将对象引用置于新类中即可。组合也是一种提高类的复用性的一种方式。如果想让类有更多的扩展功能,多用组合,少用继承
public class SoccerPlayer {
private String name;
private Soccer soccer;
}
public class Soccer {
private String soccerName;
}
-
代码中SoccerPlayer引用了Soccer类,通过引用Soccer类来达到调用soccer中属性和方法的目的
-
组合和继承的区别主要如下:
| 特征 | 组合 | 继承 |
|---|---|---|
| 关系 | 组合是一种has-a的关系,可理解为有一个 | 继承是一种is-a的关系,可理解为是一个 |
| 耦合性 | 组合的双方是一种耦合关系 | 继承双方紧耦合 |
| 是否具有多态 | 不具备多态和向上转型 | 继承是多态的基础,可实现向上转型 |
| 时期 | 组合是运行时绑定 | 继承是编译期绑定 |
面向对象11:instanceof 和 类型转换
-
instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
-
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
import java.util.ArrayList;
import java.util.Vector;
public class Main {
public static void main(String[] args) {
Object testObject = new ArrayList();
displayObjectClass(testObject);
}
public static void displayObjectClass(Object o) {
if (o instanceof Vector)
System.out.println("对象是 java.util.Vector 类的实例");
else if (o instanceof ArrayList)
System.out.println("对象是 java.util.ArrayList 类的实例");
else
System.out.println("对象是 " + o.getClass() + " 类的实例");
}
}
//结果:对象是 java.util.ArrayList 类的实例
- 类型转换
//父类:Person 子类:Student
Person obj = new Student();
//父类转换为子类
Student student = (Student)obj;
student.go();
//子类转换为父类,可能丢失自己本来的一些方法
Student students = (Student)obj;
Person objs = students;
//go方法可能丢失
objs.go();
- 父类引用指向子类的对象
- 子类转换为父类,向上转型
- 父类转换为子类,向下转型,强制转换
- 方便代码利用率,减少类的创建
面向对象12:static关键字详解
public class Student {
private static int age;//静态的变量 多线程
private double score; //非静态的变量
public void run() {
go();//非静态方法可以调用静态方法
}
public static void go() {
//run();//静态方法只能调用静态方法
}
public static void main(String[] args) {
go();
//run();
}
}
- 代码块
public class Person {
{
System.out.println("匿名代码块");
}
static {
System.out.println("静态代码块");
}
public Person() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Person s1 = new Person();
}
}
/* 结果:
静态代码块
匿名代码块
构造方法
*/
- 静态导入包
//静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(random());//直接使用random()方法
System.out.println(PI);
}
}
面向对象13:抽象类
-
abstract 定义抽象类和抽象方法,被子类单继承实现抽象方法
-
抽象方法必须放在抽象类中
抽象类中可以没有抽象方法
抽象方法只定义无方法体
一个类有大于等于一个抽象方法就是抽象类
-
子类继承抽象类必须重写抽象方法
-
抽象类不能new出对象
//abstract 抽象类: 单继承
//接口可以多继承
public abstract class Ab {
//约束方法,让子类实现方法
public abstract void doSomething();
public void sum() {
}
}
//子类继承抽象类
public class Bc extends Ab{
@Override
public void doSomething() {
}
}
面向对象14:接口的定义和实现
接口相当于是对外的一种约定和标准,Java中接口是由interface关键字来表示的,可以在内部定义方法,但不实现,接口方法隐式修饰为public abstract
//定义接口
public interface Job{
void fun();
}
接口特征:
- interface接口是一个完全抽象的类,不会提供任何方法的实现,只会进行方法的定义
- 接口中只能使用两种访问修饰符,一种是public,对整个项目可见。一种是default,缺省值,只具有包访问权限
- 接口只提供方法的定义,由实现接口的类来提供方法的实现。实现接口关键字为implement,一个接口可以有多个实现,一个类可以实现多个接口
- 实现类必须重写接口中的方法
class ZCZWriteWell implements Job {
@Override
public void fun() {
System.out.println("well");
}
}
接口默认方法
- 在接口中新增方法,意味着要在实现类中实现新增方法
- 而默认方法可以直接在接口中实现:
public interface Job{
void fun();
//默认方法,接口实现类可以不在重写该方法
default void go() {
System.out.println("default method");
}
}
面向对象15:内部类
内部类就是在一个类的内部定义另一个类,在A中类定义B类,B类就是A类的内部类,相对的,A类就是B类的外部类。
成员内部类
内部类定义在外部类成员的位置
总结
-
内部类直接调用了外部类成员方法,原因是内部类拥有外部类对象的引用。
方式:外部类名.this
-
因此内部类可以直接访问外部类成员变量
public class FirstOuterClass {
//成员内部类,定义的位置处于外部类成员的位置
private class InnerClass {
public void run() {
//内部类拥有外部类对象的引用
//所以可以直接使用外部类的成员
System.out.println(FirstOuterClass.this);
}
}
//外部类的非静态方法创建内部类对象
public InnerClass getInnerClassObj() {
return new InnerClass();
}
public static void main(String[] args) {
//创建外部类对象
FirstOuterClass firstOuterClass = new FirstOuterClass();
//1.外部类的非静态方法构造内部类
InnerClass innerClassObj = firstOuterClass.getInnerClassObj();
System.out.println(innerClassObj);
//2.借助外部类对象构造内部类
InnerClass innerClass = firstOuterClass.new InnerClass();
System.out.println(innerClass);
System.out.println(firstOuterClass);
innerClassObj.run();
}
}
成员内部类应用场景
- 为了隐藏并控制对这个类的访问
- 为了将一个类定义在另一个类的内部:当一个类依托另一类存在时
- 借助内部类实现多继承,内外部类都继承不同的类
静态内部类
- 成员内部类使用static修饰即静态内部类
- 静态内部类不能访问外部类中的非静态成员
- 静态内部类不依赖外部类对象存在,无法拥有外部类对象的引用,构造一个静态内部类无需外部类来构造
- 静态内部类中可以有静态成员,成员内部类不能有静态成员
- 创建一个静态内部类对象语法:new 外部类名.静态内部类名();
public class FirstOuterClass4 {
private int a = 0;
//静态成员
private static int b = 1;
//静态内部类
public static class FirstInnerClass {
public FirstInnerClass() {
System.out.println(b);
}
}
//返回内部类对象
public FirstInnerClass getFirstInnerClassObj() {
return new FirstInnerClass();
}
public static void main(String[] args) {
//直接构造静态内部类对象
FirstInnerClass firstInnerClass1 = new FirstInnerClass();
//创建一个外部类对象
FirstOuterClass4 firstOuterClass4 = new FirstOuterClass4();
FirstInnerClass firstInnerClass2 = firstOuterClass4.getFirstInnerClassObj();
}
}
应用场景
- 当一个类是另一个类(外部类)的附属类,它不依赖外部类对象而存在,并且需要单独在外部类之外使用,此时可以创建为静态内部类。
局部内部类
- 定义在局部代码块或方法中
- 只能在局部作用域内new出局部内部类的对象
- 特点:
- 局部内部类只能在其作用域中被实例化,不能用外部类对象实例化一个局部内部类对象。
- 局部内部类不能去使用public、private,因为局部内部类不是外部类的成员
- 局部内部类可以访问外部类对象的成员、拥有外部类对象的引用
在封闭范围内(局部内部类)使用这个范围外的局部变量只能使用常量。
注意:局部内部类中可以去定义局部变量,如果要使用它之外的局部变量,此时编译器按照final类型处理。
-
产生此限制的原因:
由于局部变量和局部内部类生命周期不同,所以编译器统一要求,如果局部内部类使用了它之外的局部变量,此时编译器按照final处理
public class FirstOuterClass2 {
//定义一个外部类的成员方法
public void getFirstInnerClassObj() {
int a = 1;
//定义局部内部类,不可使用public、private修饰
class FirstInnerClass {
//构造方法
public FirstInnerClass() {
//得到外部类对象的引用
System.out.println("外部类对象的引用"+FirstOuterClass2.this);
System.out.println(a+1);
}
//普通方法
public FirstInnerClass getInstance() {
return this;
}
}
//不能使用外部类对象构造局部内部类对象
//只能在局部内部类的作用域中new出一个局部内部类对象
FirstInnerClass firstInnerClass = new FirstInnerClass();
FirstInnerClass innerClass = firstInnerClass.getInstance();
System.out.println("局部内部类对象的引用" + innerClass);
}
public static void main(String[] args) {
//构造一个外部类对象
FirstOuterClass2 firstOuterClass2 = new FirstOuterClass2();
firstOuterClass2.getFirstInnerClassObj();
}
}
/* 结果
外部类对象的引用com.pbteach.javase.oop.innerclass.FirstOuterClass2@1b6d3586
2
局部内部类对象的引用com.pbteach.javase.oop.innerclass.FirstOuterClass2$1FirstInnerClass@4554617c
*/
匿名内部类
- 匿名内部类是一种特殊的局部内部类,没有名字。
- 定义格式:
new 父类或接口(){
//写类的成员
//重写父类或接口中的方法
};
- 特点:
- 没有名字
- 特殊的局部内部类,不能使用private、public等权限修饰
- 由于没有名字,不能定义构造方法,但可使用构造代码块实现构造方法的功能
- 无法通过外部类对象进行实例化,可以访问外部类对象的引用
- 匿名内部类要么继承一个父类,要么实现一个接口,两者不能兼备
- 匿名内部类可以访问外部类对象的成员
- 只允许使用他之外的final常量
//成员内部类
class FirstInnerClass {
//构造方法
public FirstInnerClass() {
System.out.println("外部类对象的引用" + FirstOuterClass3.this);
}
//普通方法
public FirstInnerClass getInstance() {
return this;
}
}
//定义匿名内部类,是FirstInnerClass的子类
FirstInnerClass firstInnerClass = new FirstInnerClass() {
@Override
public FirstInnerClass getInstance() {
System.out.println("匿名内部类");
System.out.println(this);
return this;
}
};