以下是作者自学Java的一些个人笔记,笔记部分由作者理解后整理(作者小白,若有理解不够到位的地方,欢迎友好讨论~),后续会考虑补全笔记,希望可以帮助到你,共勉!
Java 基础语法知识点
大小写敏感
类名:首字母大写
方法名:小写字母开头。若由多个单词组成,后面的单词首字母大写
源文件名:源文件名必须和类名相同
主方法入口:所有都是从public static void main(string[] args)方法开始执行
Java 标识符
Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
关于 Java 标识符,有以下几点需要注意:
-
所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
-
首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
-
关键字不能用作标识符
-
标识符是大小写敏感的
-
合法标识符举例:age、$salary、_value、__1_value
-
非法标识符举例:123abc、-salary
Java 修饰符
像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
-
访问控制修饰符 : default, public , protected, private
-
非访问控制修饰符 : final, abstract, static, synchronized
Java 关键字
常见的有public、class、static、void,后续再继续学习记录
Java 字面量
整数型:10、-5等
浮点型:3.14、-0.5等
布尔型:true、false
字符型:'d' 'a'等
字符串型:“Hello”、“world”、“你好”等
Java + 运算符
主要作用:求和、字符串拼接
求和:两边都是数字
字符串拼接:只要有一边是字符串
Java 变量
三要素:变量名、变量值、数据类型
int a,b,c = 300;//此处只给c赋值了300,其他a和b没有赋值
Java 作用域
变量的有效范围,在java当中通常是一个{}对应一个作用域
Java 变量分类
1.凡是在方法体当中定义的变量,一定是局部变量
局部变量只在当前方法中有效
2.在类体当中定义的变量叫做成员变量
成员变量当中,带static的是静态变量,不带static的是实例变量
进制转换(十进制、二进制、八进制、十六进制)
十进制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
八进制 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20
十进制转换成八进制:
除8取余,一直到商0为止,最后将所有的余数逆序输出
八进制转换成十进制:
每一个与权值相乘,最后求和
其余的进制转换方法大致相似,后续再进行补充
原码反码补码
0表正数,1表示负数
字节和比特
1 Byte = 8 bit
每个比特(bit)是计算机最小的存储单位,只能存储0或1两个状态
1 kb = 1024 Byte,依次类推
计算机底层采用补码的形式存储,但显示给人类的时候是以原码的形式显示
正数的原码反码补码都是相同的
负数的原码运算规则:将绝对值转换为二进制后,最高位改1
反码:以原码为参考,符号位不变,其他位取反
补码:以反码为参考,符号位不变,加1
数据类型
| 整数型 | 说明 | 字节 |
| byte | 小整数 | 1 |
| short | 中等大小整数 | 2 |
| int | Java最常用的整数 | 4 |
| long | 极大或极小的整数 | 8 |
| 浮点型 | 说明 | 字节 |
| float | 单精度,精度为7位小数 | 4 |
| double | 双精度,精度为15位小数,Java最常用的浮点类型 | 8 |
| 布尔型:boolean | 逻辑判断 | 1 |
| 字符型:char | 单个字符,’a‘等 | 2 |
整数型
java允许小容量的数据直接赋值给大容量的变量
如果在整数型字面量后面添加L,会将这个整数型字面量当作long类型来处理
例如:int a = 28234923432 这个数太大了,int 分配4个字节放不下(会报错),所以要在后面加个L
变成long a = 28234923432L 这样就没问题了
强制类型转换,编译时可能会损失精度,也可能不会损失
long x = 1000L
int y =(int)x
当一个整数型字面量没有超出byte的取值范围(-128~127)时,可以直接赋值给byte下的变量
short取值范围:-32768~32767
多种数据类型混合运算,先各自转换成容量最大的,再做运算
byte < short < int < long < float < double
浮点型
float:单精度,可以精确到7位小数
double:双精度,可以精确到15位小数
double是常用的
浮点型数据两种表示形式
1.十进制
double x = 1.23
double y = 0.23
2.科学计数法
double x = 0.123E2 // 0.123*10 的二次方
double y = 123.34E-2 // 123.34/10 的二次方
浮点型数据参与运算的结果,一定不要使用”==“与其他数字进行相等比较
主要原因:任何浮点型数据,在计算机底层存储的都是近似值
字符型
char 类型占用2个字节
char类型的字面量必须使用单引号括起来:'A' 'a' '中'
采用统一的字符编码方式:Unicode编码
char 的默认值是:\u0000
转义字符:
| \t |
制表符,相当于按下Tab键 |
| \n | 换行符 |
| \" | 双引号 |
| \' | 单引号 |
| \ \ | 反斜线本身 |
char参与的运算:
有一个运算规则需要记住:
byte short char 混合运算的时候,各自先转换成int再做运算
比如 ‘a' + 1 直接输出得到98
但是 如果 先是 char c = ’a‘ + 1 然后输出c 那就是得到b
再比如,
byte b = 1;
short s = 1;
char c = 1;
要将上面的相加起来,只能 int num = b + s + c 或者强制转换 short num = (short)(b + s + c)
布尔型
boolean 只有true和false,没有0和1的说法,用于条件判断等语句
Java String类
在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串
1.创建字符串最简单的方式如下:
String str = "Runoob";
2.用构造函数创建字符串:
String str2=new String("Runoob");
3.字符串长度表示:int len = str.length();
4.连接字符串可以使用+来进行, 比如,“你好” + “欢迎你”;
运算符
算术运算符
| 操作符 | 说明 |
| + | 加法 |
| - | 减法 |
| * | 乘法 |
| / | 整除 |
| % | 取模(求余数) |
| ++ | 自增加1 |
| -- | 自减减1 |
++出现在变量之后,先赋值再加1
++出现在变量之前,先加1再赋值
关系运算符
> >= < <= == !=
所有的关系运算符结果一定是布尔类型:true/false
= 赋值运算符
== 可以比较两个值是否相等
逻辑运算符
| 操作符 | 描述 | 例子 |
|---|---|---|
| & | 如果相对应位都是1,则结果为1,否则为0 | (A&B),得到12,即0000 1100 |
| | | 如果相对应位都是 0,则结果为 0,否则为 1 | (A | B)得到61,即 0011 1101 |
| ^ | 如果相对应位值相同,则结果为0,否则为1 | (A ^ B)得到49,即 0011 0001 |
| 〜 | 按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 | (〜A)得到-61,即1100 0011 |
| << | 按位左移运算符。左操作数按位左移右操作数指定的位数。 | A << 2得到240,即 1111 0000 |
| >> | 按位右移运算符。左操作数按位右移右操作数指定的位数。 | A >> 2得到15即 1111 |
| >>> | 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 | A>>>2得到15即0000 1111 |
条件运算符(?:)
variable x = (expression) ? value if true : value if false
public class Test {
public static void main(String[] args){
int a , b;
a = 10;
// 如果 a 等于 1 成立,则设置 b 为 20,否则为 30
b = (a == 1) ? 20 : 30;
System.out.println( "Value of b is : " + b );
// 如果 a 等于 10 成立,则设置 b 为 20,否则为 30
b = (a == 10) ? 20 : 30;
System.out.println( "Value of b is : " + b );
}
}
Java 条件语句
布尔表达式结果为true则执行,为false则不执行
//第一种语法:
if(布尔表达式)
{
//如果布尔表达式为true将执行的语句
}
//第二种语法:
if(布尔表达式){
}else{
}
//第三种语法:
if(布尔表达式 1){
else if(布尔表达式 2){
} }
Java 分支语句
switch case 语句语法格式如下:
switch(expression){
case value :
//语句
break; //可选
case value :
//语句
break; //可选
//你可以有任意数量的case语句
default : //可选
//语句
}
一般是这样的 会在switch语句外进行变量声明和赋值 expression就是变量
case 后面跟着的可以是对应等级或者1 2 3 4 反正就是你根据变量值的情况进行一个判断
break 出现后 如果变量与value相等 则停止往下执行代码
default 就是 如果以上都没有对应的情况出现的默认内容(可选,自定义)
举个例子,
public class Test {
public static void main(String args[]){
//char grade = args[0].charAt(0);
char grade = 'C';
switch(grade)
{
case 'A' :
System.out.println("优秀");
break;
case 'B' :
case 'C' :
System.out.println("良好");
break;
case 'D' :
System.out.println("及格");
break;
case 'F' :
System.out.println("你需要再努力努力");
break;
default :
System.out.println("未知等级");
}
System.out.println("你的等级是 " + grade);
}
}
运行的结果就是,
良好 你的等级是 C
Java 循环语句
while循环:
只要布尔表达式为 true,循环就会一直执行下去
while( 布尔表达式 ) {
//循环内容
}
do…while 循环:
do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次
do {
//代码语句
}while(布尔表达式);
for循环:
for(初始化; 布尔表达式; 更新) {
//代码语句
}
嵌套for循环:
这个知识点需要用到一点逻辑,就是外面那层for循环执行一次,里面的for循环执行全部,然后再进行外面的for循环,以此类推
举个例子,理解一下:
public class Test01 {
public static void main(String[] args) {
for (int i = 1; i <= 5 ; i++) {
//打印空格
for (int j = 0; j < 5 - i ; j++) {
System.out.print(" ");
}
//打印升序
for (int j = 1; j <= i ; j++) {
System.out.print(j);
}
//打印降序
for (int j = i - 1; j > 0 ; j--) {
System.out.print(j);
}
//换行
System.out.println();
}
}
//最终效果是, 1
// 121
// 12321
// 1234321
// 123454321
}
Java 增强 for 循环
for(声明语句 : 表达式)
{
//代码句子
}
声明语句:声明新的局部变量,必须跟数组元素的类型匹配
表达式:要访问的数组名
直接上例子,更有利于理解
public class Test {
public static void main(String[] args){
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ){
System.out.print( x );
System.out.print(",");
}
System.out.print("\n");
String [] names ={"James", "Larry", "Tom", "Lacy"};
for( String name : names ) {
System.out.print( name );
System.out.print(",");
}
}
}
Java 数组
Java 语言中提供的数组是用来存储固定大小的同类型元素
数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1
一般,数组变量的声明和创建数组可以用一条语句完成
dataType[] arrayRefVar = new dataType[arraySize];
//例如:
double[] myList = new double[10];
//也可以是:
double[] myList = {1,2,3};
//赋值:
myList[0] = 1;
myList[1] = 2;
处理数组
数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环或者 For-Each 循环
基本循环如下:
public class TestArray {
public static void main(String[] args) {
double[] myList = {1.9, 2.9, 3.4, 3.5};
// 打印所有数组元素
for (int i = 0; i < myList.length; i++) {
System.out.println(myList[i] + " ");
}
// 计算所有元素的总和
double total = 0;
for (int i = 0; i < myList.length; i++) {
total += myList[i];
}
System.out.println("Total is " + total);
// 查找最大元素
double max = myList[0];
for (int i = 1; i < myList.length; i++) {
if (myList[i] > max) max = myList[i];
}
System.out.println("Max is " + max);
}
}
For-Each循环
优点:代码简洁 缺点:没有下标
语法格式:
for(type element: array)
{
System.out.println(element);
}
它能在不使用下标的情况下遍历数组
实际代码如下:
public class TestArray {
public static void main(String[] args) {
double[] myList = {1.9, 2.9, 3.4, 3.5};
// 打印所有数组元素
// num代表每个元素
for (double num: myList) {
System.out.println(num);
}
}
}
数组作为函数的参数
数组可以作为参数传递给方法。
例如,下面的例子就是一个打印 int 数组中元素的方法:
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
下面例子调用 printArray 方法打印出 3,1,2,6,4 和 2:
printArray(new int[]{3, 1, 2, 6, 4, 2});
或者
package day0816;
import java.util.Scanner;
public class ArrayTest01 {
public static void main(String[] args) {
//创建好数组对象,然后传进去
int[] nums = {1,2,3,4};
display(nums);
}
public static void display(int[] arr){
for(int num : arr){
System.out.println(num);
}
}
}
数组存储的是引用
以下的Cat类、Bird类都继承了Animals类,而且Animals[ ] 其实就是引用数据类型下的数组
package day0816;
public class ArrayTest02 {
public static void main(String[] args) {
Bird b = new Bird();
Cat c = new Cat();
//创建一个数组,让该数组既可以存储Cat,又可以存储Bird
//数组中存储的不是对象本身。实际上是对象在堆内存当中的地址。存储的是引用。
Animals[] animals = {b, c};
for(Animals animal : animals){
if(animal instanceof Cat){
//向下转型
Cat cat = (Cat) animal;
cat.catchMouse();
}else if(animal instanceof Bird){
Bird bird = (Bird) animal;
bird.fly();
}
}
}
}
查找数组中最大值
package day0816;
public class ArrayTest03 {
public static void main(String[] args) {
int[] nums = {1,2,3,2,323,234,2342,234223,2123,2342,21243};
int max = searchMax(nums);
System.out.println(max);
}
public static int searchMax(int[] arr){
int max = arr[0];
for(int num : arr){
if(num > max){
max = num;
}
}
return max;
}
}
数组的反转
第一种方式:创建新数组
package day0816;
public class ArrayTest04 {
public static void main(String[] args) {
int[] num = {1,2,3,4,5,6,7,8};
reverse(num);
for(int nums : num){
System.out.println(nums);
}
}
public static void reverse(int[] arr){
int[] newArr = new int[arr.length];
for(int i = 0; i < newArr.length; i++){
newArr[i] = arr[arr.length - 1 - i];
}
for (int i = 0; i < newArr.length; i++) {
arr[i] = newArr[i];
}
}
}
第二种方式:首尾交换的方式完成反转
public class ArrayTest04 {
public static void main(String[] args) {
int[] num = {1, 2, 3, 4, 5, 6, 7, 8};
reverse(num);
for (int nums : num) {
System.out.println(nums);
}
}
public static void reverse(int[] arr) {
for (int i = 0; i < arr.length / 2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = temp;
}
}
}
main方法的String[ ] args作用
1.作用:接收命令行参数
2.JVM负责调用ArrayTest.main()方法
JVM负责给main方法准备一个String[ ] 一维数组的对象
方法的可变长度参数
1.语法格式:
数据类型 ...
2.在形参列表中,可变长参数只能有一个,并且只能在参数列表的末尾出现
3.可变长参数可以当作数组来看待
public class ArrayTest05{
public static void main(String[] args){
m1();
m1(1,2,3,4,5);
}
public static void m1(int... nums){
System.out.println("m1方法实行了");
}
}
//出现两行 m1方法实行了
//当然也可以在m1方法中进行数组的遍历
数组的扩容
1.数组的长度一旦确定不可变
2.如果数组满了,想存储更多数据,只能创建一个新的数组,将原数组中的数据全部拷贝到新数组当中。这样就可以完成扩容了
3.怎么优化?建议减少扩容次数。建议预测数据量,创建一个容量差不多的数组
4.扩容的时候需要使用到数组的拷贝,使用System.arraycopy()
package day0817;
public class ArrayTest06 {
public static void main(String[] args) {
int[] src = {11,2,4,24,24,243,243,322,121,324};
int[] des = new int[src.length * 2];
System.arraycopy(src, 0, des, 0, src.length);
for(int num : des){
System.out.println(num);
}
}
}
多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组,例如:
String[][] str = new String[3][4];
以二维数组为例
type[][] typeName = new type[typeLength1][typeLength2];
例如:
//二维数组的静态初始化
int[][] arr = new int[][]{
{1,2,3},
{4,5,6,7,8},
{1,1,2,32,4,5,3}
};
//arr[1][2] 其实等于6,也就是说前面的括号代表的是一维数组的位置
//第二个括号代表对应的数组元素的位置
//二维数组的动态初始化(等长)
int[][] a = new int[2][3];
//二维数组的动态初始化(不等长)
int[][] arr = new int[3][];
数组的优缺点
①数组优点
1.根据下标查询某个元素的效率极高。数组中有100个元素和有100万个元素,查询效率相同。时间复杂度0(1)。也就是说在数组中
根据下标查询某个元素时,不管数组的长短,耗费时间是固定不变的。
2.原因:知道首元素内存地址,元素在空间存储上内存地址又是连续的,每个元素占用空间大小相同,只要知道下标,就可以通过数学表达式计算出来要查找元素的内存地址。直接通过内存地址定位元素。
②数组缺点
1.随机增删元素的效率较低。因为随机增删元素时,为了保证数组中元素的内存地址连续,就需要涉及到后续元素的位移问题。时间复杂度0(n)。0(n)表示的是线性阶,随着问题规模n的不断增大,时间复杂度不断增大,算法的执行效率越低。(不过需要注意的是:对数组末尾元素的增删效率是不受影响的。)
2.无法存储大量数据,因为很难在内存上找到非常大的一块连续的内存。
IDEA快捷键
idea会自动保存,自动编译,不需要其他操作
生成main方法:psvm (直接缩写输入,idea会帮忙补全的)
快速生成输出语句:直接sout 或者在“......”(你想输出的内容)后面加上 .sout 比如 “你好”.sout
快速决定变量类型:在变量后面加上 .var
运行结果:ctrl + shift + F10
for循环:直接 fori idea会帮忙补全
单行注释:ctrl + /
多行注释:ctrl + shift + /
多行编辑:按alt别松手,鼠标拖动多行,完成多行编辑
怎么快速生成构造方法:alt + insert 选择Constructor
复制一行:ctrl + d
打开运行结果:alt + 4
新建/新增任何东西:alt + insert
退出任何窗口:ESC
编写源码的窗口最大化:ctrl + shift + F12
打开project窗口:alt + 1
查找某个类:敲两下shift,选择classes,输入类名
切换选项卡:alt + 左右方向键
自动生成变量:在一个值的后面输入 .var ,比如 10.var
删除一行:ctrl + y
在一个类中查找方法:ctrl + F12
Java 方法
方法定义
1.方法本质上就是一段可以被重复利用的代码片段 每个方法都是一个独立的封装好的功能
2.方法要执行的话,必须要去调用它,比如:
public class Test01 {
public static void main(String[] args){
sum(100,200) //调用方法
}
//定义方法
public static void sum(int a,int b){
int c = a + b;
System.out.println(a + "+" + b + "=" + c);
}
}
3.语法格式:[修饰符列表] 返回值类型 方法名 (形参列表){ 方法体; }
目前,修饰符列表这一块,统一编写public static,具体原因后面学习补充
返回值类型:可以是java语言中任何一种数据类型,包括基本数据类型和引用数据类型,就是byte int那些
如果方法执行结束的时候没有返回任何数据给调用者,返回类型写:void
返回类型是int,方法结束就返回一个整数给调用者
返回类型是String,方法结束就返回一个字符串给调用者
返回类型是void,方法结束就不会返回任何数据给调用者
4.当返回值类型不是void的时候,方法在结束的时候必须使用“return 值”;语句来完成数据的返回
如果是void,可以使用“return;”来结束语句,也可以不使用,绝对不可以返回一个值
5.return语句有两种写法:
第一种:return 值;
第二种:return;
不管是哪一种,只要return语句执行,方法必然结束
6.当调用一个返回值类型不是void的方法时,方法结束时会返回值,可以采用变量接收,但是类型要一致
7.方法名:一般都是首字母小写,后面的单词首字母大写
方法调用
格式:类名.方法名()
调用者 和 被调用者 在同一类的情况下,可以省略 类名.
但是在不同类的情况下,必须遵循这个格式
以下是一个登录界面,可以更好帮助理解方法的定义与调用
import java.util.Scanner;
public class Test01 {
public static void main(String[] args){
login();
}
public static void login(){
Scanner scanner = new Scanner(System.in);
System.out.println("用户名:");
String username = scanner.next();
System.out.println("密码:");
String password = scanner.next();
boolean isOk = check(username,password);
if (isOk){
System.out.println("登录成功");
}
else{
System.out.println("登陆失败");
}
}
public static boolean check(String username,String password){
return username.equals("admin") && password.equals("123456");
}
}
方法重载(overload)
方法重载,程序员使用起来很轻松
就比如,System.out.println( ) 这个方法就是算是在java.io.PrintStream类中的方法重载,我们可以往里面传各种实参,只需要记住这个方法名就好,十分方便
Java重载,即可以在同一个类中定义多个构造方法,只要参数列表不同即可
当一个程序满足怎样的条件时,代码就构成了方法重载呢
条件1:在同一个类中;
条件2:方法名一致;
条件3:形参列表不同:类型不同算不同、顺序不同算不同、个数不同算不同
//形参的个数不同
public static void m1(){ }
public static void m1(String s){ }
//形参的类型不同
public static void m2(int a,int b){ }
public static void m2(long a,long b){ }
//形参的顺序不同
public static void m3(String a,int b){ }
public static void m3(int b,String a){ }
//以下两个方法没有构成方法重载:
public static void noOverload(int a,int b){ }
public static void noOverload(int x,int y){ }
方法递归
方法递归就是方法递归调用自己
一般来说,初学者能循环就循环处理,不建议方法递归,因为递归占内存较大,没有结束语句的话,还会导致栈内存溢出错误
如果要使用,记得有结束语句
举个例子理解一下,斐波那契数列(关于一对兔子每三个月新生一对新兔子,求对应月数总数问题)
public class Test01 {
public static void main(String[] args) {
int n = 12;
int total = rabbit(n);
System.out.println(total);
}
public static int rabbit(int n) {
if(n == 1 || n == 2){
return 1;
}
return rabbit(n - 1) + rabbit(n - 2);
}
}
Java 面向对象(OO)
面向对象:关注点在实现功能需要哪些对象的参与
OOA:Object Oriented Analysis 面向对象分析
OOD:Object Oriented Design 面向对象设计
OOP:Object Oriented Programming 面向对象编程
三个特征:封装、继承、多态
人类是以面向对象的方式去认识世界
例如开汽车:汽车对象、司机对象、司机对象有一个驾驶的行为,司机对象驾驶汽车对象
类和对象
类:
①现实世界中,事物与事物之间具有共同特征,例如:刘德华和梁朝伟都有姓名、身份证号、身高等状态,都有吃、跑、跳等行为。将这些共同的状态和行为提取出来,形成了一个模板,称为类。
②类实际上是人类大脑思考总结的一个模板,类是一个抽象的概念。
③状态在程序中对应属性,属性通常用变量来表示。
④行为在程序中对应方法。用方法来描述行为动作。
⑤类=属性+方法。
对象:
①实际存在的个体。
②对象又称为实例(instance)。
③通过类这个模板可以实例化n个对象(通过类可以创造多个对象)
④例如通过“明星类”可以创造出“刘德华对象”和“梁朝伟对象”。
⑤明星类中有一个属性姓名:String name;
⑥“刘德华对象”和“梁朝伟对象”由于是通过明星类造出来的,所以这两个都有name属性,但是值是不同的。因此这种属性被称为实例变量。
类的定义:
通过类实例化对象。有了对象,让对象和对象之间协作起来形成系统
1.语法格式:
[修饰符列表] class 类名 {
//属性(描述状态)
//方法(描述行为动作)
}
2.例如:学生类
public class Student {
//姓名
String name; //实例变量
//年龄
int age;
//性别
boolean gender;
//学习
//先记住:我们通常描述一个对象的行为动作时,不加static
//没有添加static的方法,被叫做:实例方法(对象方法)
public void study( ) { System.out.println("正在学习"); } //实例方法
另起一个TestStudent01类
package day01;
public class StudentTest01 {
public static void main(String[] args) {
//通过学生类Student实例化学生对象
//Student s1;是什么?s1是变量名。Student是一种数据类型名。属于引用数据类型。
//s1也是局部变量。和i一样
//s1变量保存的是:堆内存中Student对象的内存地址
//s1有一个特殊的称呼:引用
//什么是引用?引用的本质就是一个变量,这个变量保存了java对象的内存地址
Student s1 = new Student();
//修改对象的属性(修改变量的值,给变量重新赋值)
s1.name = "张三";
s1.age = 18;
s1.gender = true;
//访问对象的属性(读变量的值)
System.out.println("姓名:" + s1.name);
System.out.println("年龄:" + s1.age);
System.out.println("性别" + (s1.gender ? "男" : "女"));
Student s2 = new Student();
s2.name = "李四";
s2.age = 20;
s2.gender = false;
System.out.println("姓名:" + s2.name);
System.out.println("年龄:" + s2.age);
System.out.println("性别" + (s2.gender ? "男" : "女"));
}
}
两种访问对象的方式:第一种读取,第二种修改。
name age gender 是实例变量(对象变量),要访问就需要先有个对象,即new个对象 对象.
读取:引用.变量名 s1.name;s1.age;s1.gender
修改:引用.变量名 = 值;s1.name = "jack"; s1.age = 20;
需要注意的是,如果不给变量赋值,那么输出的就是变量的默认值
| 数据类型 | 默认值 |
|---|---|
| int | 0 |
| long | 0L |
| short | 0 |
| char | '\u0000' |
| byte | 0 |
| float | 0.0f |
| double | 0.0d |
| boolean | false |
| 引用类型(类、接口、数组) | null |
还有一个点先记住:我们通常描述一个对象的行为动作时,不加static
没有添加static的方法,被叫做:实例方法(对象方法),需要"引用."来实现
this关键字
大部分情况下可以省略,但是涉及到setter方法中变量区分的时候不可省略
this不能出现在静态方法(有static的方法)当中
this 可以有助于外部变量动态的进行访问
public class Student {
//属性:实例变量,学生姓名
String name;
//方法:学习的行为(实例方法)
public void study(){
//this 本质上是一个引用
//this 中保存的也是对象的内存地址
//this 中保存的是当前对象的内存地址
System.out.println(this.name + "正在努力的学习!!!");
}
}
this(实参):
1.通过这种语法可以在构造方法中调用本类中其他的构造方法
2.作用:代码复用
3.this(实参);只能出现在构造方法第一行
static关键字
1.static 修饰的变量叫做静态变量。当所有对象的某个属性的值是相同的,建议将该属性定义为静态变量,来节省内存的开销
2.static 修饰的方法叫做静态方法
3.所有静态变量和静态方法,统一使用“类名.”调用,不需要new
4.静态方法中不能使用this关键字。因此无法直接访问实例变量和调用实例方法
5.一个类中可以编写多个静态代码块,遵循自上而下的顺序依次执行
6.“引用."可以访问到静态变量,但不建议
7.静态代码块,只有在实际开发业务当中才会用得到,在类加载的时间节点上
封装
为了保证User类型对象的age属性的安全,我们需要使用封装机制,实现封装的步骤是什么?
第一步:属性私有化(使用 private 进行修饰 变量)
属性私有化的作用是:禁止外部程序对该属性进行随意的访问
所有被private修饰的,都是私有的,只能在本类当中访问
第二步:对外提供setter和getter方法
为了保证外部的程序仍然可以访问age属性,因此要对外提供公开的访问入口
访问一般包括两种:
读:读取属性的值
改:修改属性的值
那么应该对外提供两个方法:一个负责读(getter),一个负责修改(setter)
读方法的格式:getter
比如:public int getAge(){ }
改方法的格式:setter
比如:public void setAge(int age){ }
还有一个值得注意的点:关于this的
public void setAge(int age){
//this. 大部分情况下可以省略
//this. 什么时候不能省略?用来区分局部变量和实例变量的时候
this.age = age;
}
继承
1.作用:代码复用
重要作用:有了继承,才有了方法覆盖和多态机制
2.继承在java中如何实现?
[修饰符列表] class 类名 extends 父类名{ }
extends 翻译为扩展,表示子类继承父类后,子类是对父类的扩展
3.Java只支持单继承,一个类只能直接继承一个类
4.Java不支持多继承,但支持多重继承(多层继承)
5.子类继承父类后,除私有的不支持继承、构造方法不支持继承。其他的全部会继承。
6.一个类没有显示继承任何类时,默认继承java.lang.Object类
比如,
公共父类:动物类
public class Animal {
private String name;
private int id;
public Animal(String myName, int myid) {
name = myName;
id = myid;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void sleep(){
System.out.println(name+"正在睡");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}
子类:老鼠类:
public class Penguin extends Animal {
public Penguin(String myName, int myid) {
super(myName, myid);
}
}
继承的特性
-
子类不继承父类中私有的方法、属性,不继承构造方法
-
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
-
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)
覆盖(Override)
重写(Override)是指子类定义了一个与其父类中具有相同名称、参数列表和返回类型的方法,并且子类方法的实现覆盖了父类方法的实现。 即外壳不变,核心重写!
访问权限不能变低,可以变高
抛出异常不能变多,可以变少
私有的方法,以及构造方法不能继承
方法覆盖针对的是实例方法,和静态方法无关,和实例变量没有关系
例如:
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public void move(){
System.out.println("狗可以跑和走");
}
}
public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
}
}
多态
编译阶段是一种形态,运行的时候是另一种形态,因此得名:多态
向上转型(upcasting):
1.子 --> 父
2.前提是两种类型之间要有继承关系
3.父类型引用指向子类型对象。这个就是多态机制最核心的语法
比如,Animal类和Cat类是继承关系,
//省略了Animal类和Cat类的具体代码,不重要,以下是Test类的部分核心代码
Animal a2 = new Cat();
a2.move();
第一阶段:编译阶段
在编译的时候,编译器只知道a2类型是Animal类型
因此在编译的时候就会去Animal类中找move()方法
找到之后,绑定上去,此时发生静态绑定。能够绑定成功,表示编译通过
第二阶段:运行阶段
在运行的时候,堆内存中真实的java对象是Cat类型
所以move()的行为一定是Cat对象发生的
因此运行的时候会自动调用Cat对象的move()方法
这种绑定称为动态绑定
向下转型(downcasting):
1.父 --> 子
2.父类型的引用可以转换为子类型的引用,但是需要加强制类型转换符
3.需要注意的是,建议使用instanceof运算符进行判断来避免ClassCastException的发生
instanceof运算符的使用
1.语法格式:引用 instanceof 类型
2.执行结果是true或者false
3.例如:(a instanceof Cat)
如果结果是true:表示a引用指向的对象是Cat类型的
如果结果是false:表示a引用指向的对象不是Cat类型的
方法覆盖和多态联合起来才有意义
静态方法本身和多态就是没有关系,因为多态机制需要对象的参与
静态方法既然和多态没有关系,那么静态方法也就和方法覆盖没有关系了
抽象类
1.当开发者不知道父类中的方法要实现什么功能时,可以把这个方法变成抽象方法,在子类当中进行重写
2.抽象方法的定义:修饰符列表中添加abstract,然后不能有方法体,以“;”结束
比如,public abstract void eat()
3.与此同时这个类也必须变成抽象类
抽象类 public abstract class Person{ ... }
4.public 和 abstract关键字的顺序没有要求
5.抽象类有构造方法,但无法实例化。抽象类的构造方法是给子类使用的
6.抽象类不一定有抽象方法,但如果有抽象方法那么类要求必须是抽象类
7.abstract关键字不能和private,final,static关键字共存
接口
①接口(interface)在Java中表示一种规范或契约,它定义了一组抽象方法和常量,用来描述一些实现这个接口的类应该具有哪些行为和属性。接口和类一样,也是一种引用数据类型。
②接口怎么定义?
[修饰符列表]interface 接口名{}
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包
public interface NameOfInterface
{
//任何类型 final, static 字段
//抽象方法
}
接口有以下特性:
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
- 接口中的方法都是公有的。
实例:
/* 文件名 : Animal.java */
interface Animal {
public void eat();
public void travel();
}
接口的实现
类使用implements关键字实现接口,一个类可以实现多个接口。语法是:class 类 implements 接口A,接口B{}
实例:
/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{
public void eat(){
System.out.println("Mammal eats");
}
public void travel(){
System.out.println("Mammal travels");
}
public int noOfLegs(){
return 0;
}
public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
接口的继承
和类的继承相似。接口的继承使用extends关键字,子接口继承父接口的方法
实例:
// 文件名: Sports.java
public interface Sports
{
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
// 文件名: Football.java
public interface Football extends Sports
{
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
// 文件名: Hockey.java
public interface Hockey extends Sports
{
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
接口的作用
①面向接口调用的称为:接口调用者
2)面向接口实现的称为:接口实现者
③调用者和实现者通过接口达到了解耦合。也就是说调用者不需要关心具体的实现者,实现者也不需要关心具体的调用者,双方都遵循规范,面向接口进行开发。
面向抽象编程,面向接口编程,可以降低程序的耦合度,提高程序的扩展力。
⑤例如定义一个Usb接口,提供read()和write()方法,通过read()方法读,通过write()方法写:①定义一个电脑类Computer,它是调用者,面向Usb接口来调用。
②Usb接口的实现可以有很多,例如:打印机(Printer),硬盘(HardDrive)。
public class Computer{
public void conn(Usb usb){
usb. read ();
usb. write ();
}
⑦再想想,我们平时去饭店吃饭,这个场景中有没有接口呢?食谱菜单就是接口。顾客是调用者。厨师是实现者。
③抽象类是半抽象的,接口是完全抽象的。接口没有构造方法,也无法实例化。
④(JDK8之前)接口中只能定义:常量+抽象方法。接口中的常量的static final可以省略。接口中的抽象方法的abstract可以省略。接口中所有的方法和变量都是public修饰的。
⑤接口和接口之间可以多继承。
⑥类和接口的关系我们叫做实现(这里的实现也可以等同看做继承)。使用implements关键字进行接口的实现。
⑦一个非抽象的类实现接口必须将接口中所有的抽象方法全部实现。
⑧Java8之后,接口中允许出现默认方法和静态方法(JDK8新特性),Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法
⑨引入默认方式是为了解决接口演变问题:接口可以定义抽象方法,但是不能实现这些方法。所有实现接口的类都必须实现这些抽象方法。这会导致接口升级的问题:当我们向接口添加或删除一个抽象方法时,这会破坏该接口的所有实现,并且所有该接口的用户都必须修改其代码才能适应更改。这就是所谓的“接口演变”问题。
⑩引入的静态方法只能使用本接口名来访问,无法使用实现类的类名访问。
JDK9之后允许接口中定义私有的实例方法(为默认方法服务的)和私有的静态方法(为静态方法服务的)。
所有的接口隐式的继承Object。因此接口也可以调用Object类的相关方法。
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
接口与抽象类区别
抽象类和接口虽然在代码角度都能达到同样的效果,但适用场景不同:
①抽象类主要适用于公共代码的提取。当多个类中有共同的属性和方法时,为了达到代码的复用,建议为这几个类提取出来一个父类,在该父类中编写公共的代码。如果有一些方法无法在该类中实现,可以延迟到子类中实现。这样的类就应该使用抽象类。
②接口主要用于功能的扩展。例如有很多类,一些类需要这个方法,另外一些类不需要这个方法时,可以将该方法定义到接口中。需要这个方法的类就去实现这个接口,不需要这个方法的就可以不实现这个接口。接口主要规定的是行为。
super关键字
①super关键字和this关键字对比来学习。this代表的是当前对象。super代表的是当前对象中的父类型特征。
②super不能使用在静态上下文中。
③“super.”大部分情况下是可以省略的。什么时候不能省略?
当父类和子类中定义了相同的属性(实例变量)或者相同方法(实例方法)时,如果需要在子类中访问父类的属性或方法时,super.不能省略
④this可以单独输出,super不能单独输出。
⑤super(实参);通过子类的构造方法调用父类的构造方法,目的是为了完成父类型特征的初始化。
⑥当一个构造方法第一行没有显示的调用“super(实参);”,也没有显示的调用“this(实参)”,系统会自动调用super()。因此一个类中的无参数构造方法建议显示的定义出来。
final关键字
1.final表示最终的,不可变的
2.final修饰的类无法被继承
3.final修饰的方法无法覆盖
4.final修饰的变量一旦赋值,不能重新赋值
5.final修饰的实例变量一般和static联合使用,这就是著名的:常量
6.final修饰的引用:一旦指向了某个对象,则不能再指向其它对象,但指向的对象内部的数据是可以修改的,比如使用setName()方法等
常量
常量的定义:public static final 数据类型 常量名 = 常量值;
常量名的命名规范:全部单词大写,每个单词采用“_”衔接
构造方法
1.构造方法有什么用?
作用1:对象的创建(通过调用构造方法可以完成对象的创建)
作用2:对象的初始化(给对象的所有属性赋值就是对象的初始化)
2.怎么定义构造方法?
[修饰符列表] 构造方法名(形参列表){
构造方法体;
}
比如 public Student(){
}
注意:构造方法名必须和类名一致
构造方法不需要提供返回值类型
如果提供了返回值类型,那么这个方法就不是构造方法了,变成普通方法了
3.构造方法怎么调用呢?
使用new运算符来调用
语法:new 构造方法名(实参)
注意:构造方法最终执行结束之后,会自动将创建的对象的内存地址返回。但构造方法体中不需要提供”return 值;“ 这样的语句
4.在java语言中,如果一个类没有显示的去定义构造方法,系统会提供一个无参数的构造方法(往往叫做缺省构造器)
5.一个类中如果显示定义了构造方法,系统则不再提供缺省构造器,所以为了对象创建更加方便,建议把无参数构造方法手动写出来
6.在java中,一个类可以定义多个构造方法,而且这些构造方法自动构成了方法的重载(overload)
7.构造方法中给属性赋值了?为什么还需要单独定义set方法给属性赋值呢?
在构造方法中赋值是对象第一次创建时属性赋的值。set方法可以在后期的时候调用,来完成属性值的修改
8.执行方法执行原理?
对象的创建在构造方法体执行之前就完成了
9.构造代码块
语法格式: { } (就只有两个大括号)
什么时候执行构造代码块?
在每一次new的时候,都会先执行一次构造代码块
10.构造代码块有什么用?
如果所有的构造方法在最开始的时候有相同的一部分代码,不妨将这个公共的代码提取到构造代码块当中去,这样代码就可以得到复用
访问控制权限
| 访问权限修饰符 | ||||
| 修饰符 | 同一个类 | 同一个包 | 子类 | 所有类 |
| private | √ | |||
| 缺省 | √ | √ | ||
| protected | √ | √ | √ | |
| public | √ | √ | √ | √ |
①private:私有的,只能在本类中访问。
②缺省:默认的,同一个包下可以访问。
③protected:受保护的,子类中可以访问。(受保护的通常就是给子孙用的。)
④public:公共的,在任何位置都可以访问。
①类中的属性和方法访问权限共有四种:private、缺省、protected和public。
②类的访问权限只有两种:public和缺省。
③访问权限控制符不能修饰局部变量。
设计模式
①什么是设计模式?
设计模式(Design Pattern)是一套被广泛接受的、经过试验验证的、可反复使用的基于面向对象的软件设计经验总结,它是软件开发人员在软件设计中,对常见问题的解决方案的总结和抽象。设计模式是针对软件开发中常见问题和模式的通用解决方案
②设计模式有哪些?
1. GoF设计模式:《Design Patterns: Elements of Reusable Object-Oriented Software》(即后述《设计模式》一书),由 Erich Gamma,Richard Helm、 Ralph Johnson 和 John Vlissides 合著(Addison-Wesley,1995)。这几位作者常被称为四人组(Gang of Four)。
2.架构设计模式(Architectural Pattern):主要用于软件系统的整体架构设计,包括多层架构、MVC架构、微服务架构、REST架构和大数据架构等。
3.企业级设计模式(Enterprise Pattern):主要用于企业级应用程序设计,包括基于服务的架构(SOA)、企业集成模式(EIP)、业务流程建模(BPM)和企业规则引擎(BRE)等。
4.领域驱动设计模式(Domain Driven Design Pattern):主要用于领域建模和开发,包括聚合、实体、值对象、领域事件和领域服务等。
5.并发设计模式(Concurrency Pattern):主要用于处理并发性问题,包括互斥、线程池、管道、多线程算法和Actor模型等。
6.数据访问模式(Data Access Pattern):主要用于处理数据访问层次结构,包括数据访问对象(DAO)、仓库模式和活动记录模式等。③GoF设计模式的分类?
1.创建型:主要解决对象的创建问题
2.结构型:通过设计和构建对象之间的关系,以达到更好的重用性、扩展性和灵活性
3.行为型:主要用于处理对象之间的算法和责任分配
Java 单例模式
Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在,并提供一个访问它的全局访问点
单例模式如何实现?
第一步:构造方法私有化
第二步:对外提供一个公开的静态的方法,用这个方法获取单个实例
第三步:定义一个静态变量,在类加载的时候,初始化静态变量(只初始化一次)
饿汉模式
类加载时对象就创建好了,不管这个对象用还是不用
// 饿汉模式
public class Singleton {
private static Singleton instance=new Singleton();// 自行创建实例
private Singleton(){}// 构造函数
public static Singleton getInstance(){// 通过该函数向整个系统提供实例
return instance;
}
}
懒汉模式
用到这个对象的时候再创建这个对象
// 懒汉模式
public final class Singleton {
private static Singleton instance= null;// 不实例化
private Singleton(){}// 构造函数
public static Singleton getInstance(){// 通过该函数向整个系统提供实例
if(null == instance){// 当 instance 为 null 时,则实例化对象,否则直接返回对象
instance = new Singleton();// 实例化对象
}
return instance;// 返回已存在的对象
}
}
软件开发七大原则
①软件开发原则旨在引导软件行业的从业者在代码设计和开发过程中,遵循一些基本原则,以达到高质量、易维护、易扩展、安全性强等目标。软件开发原则与具体的编程语言无关的,属于软件设计方面的知识。
②软件开发七大原则
开闭原则(Open—Closed Principle,OCP):一个软件实体应该对扩展开放,对修改关闭。即在不修改原有代码的基础上,通过添加新的代码来扩展功能(最基本的原则,其它原则都是为这个原则服务的。)
2.单一职责原则:一个类只负责单一的职责,也就是一个类只有一个引起它变化的原因。
3.里氏替换原则:子类对象可以替换其基类对象出现的任何地方,并且保证原有程序的正确性。4.接口隔离原则:客户端不应该依赖它不需要的接口。
5.依赖倒置原则:高层模块不应该依赖底层模块,它们都应该依赖于抽象接口。换言之,面向接口编程。
6.迪米特法则:一个对象应该对其它对象保持最少的了解。即一个类应该对自己需要耦合或调用的类知道得最少。
7.合成复用原则:尽量使用对象组合和聚合,而不是继承来达到复用的目的。组合和聚合可以在获取外部对象的方法中被调用,是一种运行时关联,而继承则是一种编译时关联。
多态在开发中的作用
①降低程序的耦合度,提高程序的扩展力。
②尽量使用多态,面向抽象编程,不要面向具体编程。
UML
① UML(Unified Modeling Language,统一建模语言)是一种用于面向对象软件开发的图形化的建模语言。它由Grady Booch、James Rumbaugh和lvar Jacobson等三位著名的软件工程师所开发,并于1997年正式发布。UML提供了一套通用的图形化符号和规范,帮助开发人员以图形化的形式表达软件设计和编写的所有关键方面,从而更好地展示软件系统的设计和实现过程。
②UML是一种图形化的语言,类似于现实生活中建筑工程师画的建筑图纸,图纸上有特定的符号代表特殊的含义。
③UML不是专门为java语言准备的。只要是面向对象的编程语言,开发前的设计,都需要画UML图进行系统设计。(设计模式、软件开发七大原则等同样也不是只为java语言准备的。)
4UML图包括:
类图(Class Diagram):描述软件系统中的类、接口、关系和其属性等;
用例图(Use Case Diagram):描述系统的功能需求和用户与系统之间的关系;
序列图(Sequence Diagram):描述对象之间的交互、消息传递和时序约束等;
状态图(Statechart Diagram):描述类或对象的生命周期以及状态之间的转换;
对象图(Object Diagram):表示特定时间的系统状态,并显示其包含的对象及其属性;
协作图(Collaboration Diagram):描述对象之间的协作,表示对象之间相互合作来完成任务的关系;活动图(Activity Diagram):描述系统的动态行为和流程,包括控制流和对象流;
部署图(Deployment Diagram):描述软件或系统在不同物理设备上部署的情况,包括计算机、网络、中间件、应用程序等。⑤常见的UML建模工具有:StarUML,Rational Rose等。
Object类
①java.lang.Object是所有类的超类。java中所有类都实现了这个类中的方法。
②Object类是我们学习JDK类库的第一个类。通过这个类的学习要求掌握会查阅API帮助文档。③现阶段Object类中需要掌握的方法:
toString:将java对象转换成字符串。
equals:判断两个对象是否相等。
字符串的比较需要用到equals()方法,即a.equals(b)
toString和equals都要进行重写
④现阶段Object类中需要了解的方法:
hashCode:返回一个对象的哈希值,通常作为在哈希表中查找该对象的键值。Object类的默认实现是根据对象的内存地址生成一个哈希码(即将对象的内存地址转换为整数作为哈希值)。hashCode()方法是为了HashMap、Hashtable、HashSet等集合类进行优化而设置的,以便更快地查找和存储对象。
finalize:当java对象被回收时,由GC自动调用被回收对象的finalize方法,通常在该方法中完成销毁前的准备。
clone:对象的拷贝。(浅拷贝,深拷贝)
protected修饰的只能在同一个包下或者子类中访问。
只有实现了Cloneable接口的对象才能被克隆。
内部类
①什么是内部类?
定义在一个类中的类
②什么时候使用内部类?
一个类用到了另外一个类,而这两个类的联系比较密切,但是如果把这两个类定义为独立的类,不但增加了类的数量,也不利于代码的阅读和维护。
内部类可以访问外部类的私有成员,这样可以将相关的类和接口隐藏在外部类的内部,从而提高封装性。
匿名内部类是指没有名字的内部类,通常用于定义一个只使用一次的类,比如在事件处理中。
③内部类包括哪几种?
静态内部类:和静态变量一个级别
①静态内部类如何实例化:OuterClass.StaticInnerClass staticlnnerClass=new OuterClass. StaticInnerClass();
②无法直接访问外部类中实例变量和实例方法。
实例内部类:和实例变量一个级别
①实例内部类如何实例化:OuterClass.InnerClass innerClass=new OuterClass(). new InnerClass();
②可以直接访问外部类中所有的实例变量,实例方法,静态变量,静态方法。
局部内部类:和局部变量一个级别
①局部内部类方外类外部的局部变量时,局部变量需要被final修饰。
②从JDK8开始,不需要手动添加final了,但JVM会自动添加。
匿名内部类:特殊的局部内部类,没有名字,只能用一次。
类的关系
1.泛化关系(is a) “extends”
2.实现关系(is like a) “implements”
3.关联关系(has a) “共存”
4.聚合关系
聚合关系指的是一个类包含、合成或者拥有另一个类的实例,而这个实例是可以独立存在的。聚合关系是一种弱关联关系,表示整体与部分之间的关系。例如一个教室有多个学生
5.组合关系(Composition)
组合关系是聚合关系的一种特殊情况,表示整体与部分之间的关系更加强烈。组合关系指的是一个类包含、合成或者拥有另一个类的实例,而这个实例只能同时存在于一个整体对象中。如果整体对象被销毁,那么部分对象也会被销毁。例如一个人对应四个肢体。
6.依赖关系(Dependency)
依赖关系是一种临时性的关系,当一个类使用另一个类的功能时,就会产生依赖关系。如果一个类的改变会影响到另一个类的功能,那么这两个类之间就存在依赖关系。依赖关系是一种较弱的关系,可以存在多个依赖于同一个类的对象。例如A类中使用了B类,但是B类作为A类的方法参数或者局部变量等。
3028

被折叠的 条评论
为什么被折叠?



