JavaSE基础(已完结)

文章目录


前言

这个博文仅用于个人记录学习过程,学习心得,以便日后复习。本人使用的网课教程来源于
Java零基础教程视频(适合Java 0基础,Java初学入门)


一、HelloWorld源程序

展示了Java源程序的基本结构
注:
    System.out.println();   //输出以后换行
    System.out.print();   //输出以后不换行

代码实例:HelloWorld源程序
/*
	1、Java中任何有效的代码必须写到“类体”当中,最外层必须是一个类的定义。
	2、public表示公开的,class表示定义一个类,HelloWorld是一个类名。类名后面必须是一对大括号,这一对大括号被称为“类体”
	3、括号必须是要成对的,建议成对编写。
	4、Java对缩进不严格,缩进是为了更好的阅读
*/

public class HelloWorld {  //声明(定义)一个公开的类,名字叫HelloWorld
	//类体

	/*
	 	1、整个这一块代码被称为main方法(主方法)(程序的入口),任何程序都要有一个入口。
	 	2、方法必须要放到“类体”中。
	 	3、JVM在执行程序的时候,会主动去找这样一个方法。
	 */
	public static void main(String[] args){//这是一个入口方法,arges是可以更改的
		//方法体
		/*
		 	注意:
		 	1、方法体由一行一行的“Java语句”构成。
		 	2、任何一条Java语句都必须以英文“;”结尾,“;”表示一条语句的结束。
		 	3、方法体中的代码遵循自上而下的顺序逐行执行。
		 */
		
		System.out.println("HelloWorld!");//向控制台输出“HelloWorld!”
		System.out.println(50);//数字
		System.out.println(1+1);//公式计算,加减乘除都可
		System.out.println("1+1");//字符串		
		
		}
	}

	

二、类体

1、一个Java源文件可以定义多个class。
2、在源文件只要有一个class的定义,就会对应生成一个class文件。
3、public类不是必须的,可以没有。若有,其有且只有一个。
4、public修饰的类名必须和源文件名保持一致。
5、每个类中都可以编写一个入口。

代码实例:
public class Class_Knowledge_Point {
	
}

class A{
	public static void main(String[] args) {
		
	}
}

class B{
	
}

class C{
	
}

三、标识符(与C语言类似)

Java严格区分大小写

3.1 标识符的命名规则

1.1 标识符由数字、字母(包括中文)、下划线、美元符号组成
1.2 标识符不能以数字进行开头
1.3 关键字不能做标识符
1.4 标识符严格区分大小写

3.2 标识符的命名规范

2.1 见名知意
2.2 驼峰命名法
2.3 类名,接口名的特殊要求:首字母大写,后面每个单词首字母大写
2.4 变量名,方法名的特殊要求:首字母小写,后面每个单词首字母大写
2.5 所有常量名每个字母都大写,单词之间采用“_”相连


四、变量(与C语言类似)

4.1 字面量

字面量:在Java语言中,数据被称为字面量

4.2 变量

变量:用于存储数据且值可以改变
变量三要素:数据类型、变量名、值
变量声明/定义的语法格式(与C语言类似):
数据类型 变量名;
例:int nianLing;(ps:Java语言有规定,变量必须先声明,再赋值才可以访问)
同一个域中变量不能重复声明

代码实例:
public class Variate {
	public static void main(String[] args) {
		int nianLing = 20; //变量的定义与赋值
		int a,b,c =100; //Java一行可以声明多个变量,但是a,b没有赋值,只有c赋值了100
		System.out.println(nianLing);
		
		/*
		 	若直接写	System.out.println(a);
					System.out.println(b);
		 	则会出现错误:
		 	The local variable a may not have been initialized
			The local variable b may not have been initialized
			原因是:a,b没有赋值
		*/
		
		a = 100;
		b = 100;
		System.out.println(a);
		System.out.println(b);
		System.out.println(c);
	}

}

4.3、变量的作用域

4.3.1 变量的分类

根据变量的出现的位置划分:
  在方法体中声明的变量叫做:局部变量
    注:局部变量只在方法体中有效,方法体执行结束后该变量的内存就释放了。
  在方法体外,类体内声明的变量叫做:成员变量

4.3.2 变量的作用域

变量的作用域是变量的有效范围(出了大括号就不认识了)
访问:Java有就近原则

代码实例:作用域
public class ZuoYongYu {
	int a=1000;//成员变量
	public static void main(String[] args) {
		int a=100;//局部变量,作用域是main函数
		System.out.println(a);//就近原则,输出100
		
		for(int k=0;k<10;k++) {//k的作用域是for函数
			
		}
		/*
		 	若编写 System.out.println(k);则会报错
		 	原因是k的作用域是for函数
		 */
		
		int i;
		for(i=0;i<10;i++) {
			
		}
		System.out.println(i);
	}
}

五、Java数据类型

5.1 数据类型

5.1.1 数据类型的作用

数据类型用来声明变量,程序在运行过程中根据不同的数据类型分配不同大小的空间。

5.1.2 数据类型的分类

第一种:基本数据类型
  整数型
byte(占用1个字节),int(占用2个字节),short(占用4个字节),long(占用8个字节)
  浮点型
float(占用4个字节),double(占用8个字节)
  布尔型
boolean(占用1个字节)
  字符型
char(占用2个字节) (Java中规定字符型字面量必须使用单引号括起来)
第二种:引用数据类型

Java中的数据类型

5.1.2.1 整数型

①在Java语言中,整数型字面量有4种表示形式,分别是:二进制,八进制,十进制(最常用),十六进制。
②在Java语言中整数型的字面量默认被当作int类型处理,若希望该“整数型字面量”被当作long类型处理,需要在其后面加L/l。
③int不超范围可以直接赋值给byte,short,char,并且会做自动类型转换。
④多种数据类型做混合运算时,最终结果的类型是最大容量对应的类型,但是byte,short,char数据类型做混合运算时,会各自转化成int在做运算。

代码实例:
public class IntTest {
	public static void main(String[] args) {
		int a = 10; //十进制表示 
		System.out.println(a); //输出10
		int b = 010; //八进制表示,0....
		System.out.println(b); //输出8
		int c = 0x10; //十六进制表示,0x...
		System.out.println(c); //输出16
		int d = 0b10; //二进制表示,0b...
		System.out.println(d); //输出2

		char c1 = 97; //int不超范围可以直接赋值给char,并且会做自动类型转换
		System.out.println(c1); //输出a

		char c2 = 'A';
		byte b1 = 2;
		System.out.println(c2 + b1);
		
		/*
		 	若写 short x = c2 + b1;
		 	则会报错 错误原因是c2 + b1 运算以后是int型的,不能直接转化为short型的
		 */

	}
}

5.1.1.2 浮点型

Java中规定任何一个浮点型数据都默认被当作double型来处理。如果想让这个浮点型字面量被当作float型来处理,那么要在这个字面量后加F/f

5.1.1.3 布尔型

在Java语言中,boolean类型只有两个值:true和false;不像C或者C++,C语言中1和0也可以表示布尔型

5.2 类型转换

类型转换规则
①八种基本数据类型中,除 boolean类型不能转换,剩下七种类型之间都可以进行转换。
②如果整数型字面量没有超出 byte,short,char的取值范围,可以直接将其赋值给byte,short,char类型的变量。
③小容量向大容量转换称为自动类型转换,容量从小到大的排序为: byte < short(char)<int < long< float< double,其中 short和 char 都占用两个字节,但是char可以表示更大的正整数。
④大容量转换成小容量,称为强制类型转换,编写时必须添加“强制类型转换符”,但运行时可能出现精度损失,谨慎使用。
⑤byte,short,char类型混合运算时,先各自转换成int类型再做运算。
⑥多种数据类型混合运算,各自先转换成容量最大的那一种再做运算。

5.3 转义字符

\n :表示换行
\t :表示tab键
\\ :表示普通的反斜杠
\" :表示普通的双引号
\’ :表示普通的单引号
\u :表示后面的是一个字符unicode编码


六、运算符(和C语言很相似)

6.1 算数运算符

 +,-,*,/,%,++(自加一),-- (自减一)

主要注意++和-- 的位置

代码实例:a++与++a
public class Operator {
	public static void main(String[] args) {
		int a = 10;
		System.out.println(a++); //输出10
		//上面语句可以拆分为
		//int temp = a++;
		//System.out.println(temp);
		System.out.println(a); //输出11
		
		int b = 20;
		System.out.println(++b); //输出21
		System.out.println(b);	//输出21	
	}
}

6.2 关系运算符

 <,<=,>,>=,==,!=

6.3 逻辑运算符

	逻辑与:&
	逻辑或:|
	逻辑非:!
	短路与:&&
	短路非:||

  短路与和短路非具有短路现象:&&左边为false时,||左边为true时右边表达式不执行。(结果确定时,右边表达式不执行)
  在开发中,大部分情况下用&&,||,只有当两边表达式均执行时,用&,|

代码实例:短路现象代码
/*
 	短路现象代码
 */
public class Operator {
	public static void main(String[] args) {
		int x = 20;
		int y = 21;
		System.out.println(x > y & x > y++);
		System.out.println(y); //输出22
		
		int m = 20;
		int n = 21;
		System.out.println(m > n && m > n++);
		System.out.println(n); 输出21
	}
}

6.4 赋值运算符

=,+=,-=,*=,/=,%=,^=,&=,|=,<<=,>>=

x += 10类似于x = x + 10;但是二者不完全相同
Java中的语法机制:使用扩展运算符时,永远都不会改变运算结果类型。

代码实例:类型转换与赋值运算
public class Operator {
	public static void main(String[] args) {
		byte b1 =100;
		byte b2 =100;
		/* 
		 	若写:b1 = b1 + 1;
		 	则会报错,因为int不能直接转换为byte
		 */
		b1 += 1;
		b2 += 105//虽然超出范围了,但是不会报错,会损失精度
		System.out.println(b1); //输出101
		System.out.println(b2); //输出-51
	}
}

6.5条件运算符(三目)

语法格式:布尔表达式 ? 表达式1 : 表达式2

运行原理:当布尔表达式值为true时,表达式1的结果作为整个表达式的结果;当布尔表达式值为false时,表达式2 的结果作为整个表达式的结果。

代码实例:三目运算
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 ); 
}
 }

6.6 字符串连接运算符(+)

当 + 运算符任意一边为字符串类型时,那么就会进行字符串的拼接操作。
当表达式中有多个 + 号时,遵循自左向右依次执行

public class Operator {
	public static void main(String[] args) {
		int a = 100;
		int b = 200;
		int nianLing = 20;
		System.out.println("年龄=" + nianLing); //输出 年龄=20
		System.out.println(a + b + "50"); //输出30050
	}
}

6.7 运算符的优先级

Java运算符的优先级


七、控制语句(和C语言基本相同)

7.1 选择(分支)语句

7.1.1 if语句

所有的if语句,一个分支执行,整个语句结束。

语法格式
if (布尔表达式1) {
	语句块1;
} else if (布尔表达式2) {
	语句块2;
} else if (布尔表达式3) {
	语句块3;
}……else(布尔表达式n) {
	语句块n;


代码实例:根据年龄判断人所处的时期
/*
 	根据年龄判断人所处的时期
 	要求:
 		1.从键盘上接收一个人的年龄
 		2.年龄范围为[0,135],超过范围提示非法信息
 		3.[0,6] 婴幼儿
 		  [7,17] 少年
 		  [18,40] 青年
 		  [41,65] 中年
 		  [66,135] 老年
 */
public class IfTest {
	public static void main(String[] args) {
		java.util.Scanner s=new java.util.Scanner(System.in);
		
		System.out.print("请输入年龄:");
		int age = s.nextInt();
		
		if(age < 0 || age > 135) {
			System.out.println("输入年龄非法!");
		}
		else if(age <= 6) {
			System.out.println("婴幼儿");
		}
		else if( age <= 17) {
			System.out.println("少年");
		}
		else if( age <= 40) {
			System.out.println("青年");
		}
		else if( age <= 65) {
			System.out.println("中年");
		}
		else {
			System.out.println("老年");
		}
		
	}
}

7.1.2 switch语句

语法格式
switch (值) {
	case 值1:
		语句块1;
		break;
	case 值2:
		语句块2;
		break;
	……
	case 值n:
		语句块n;
		break;
	default:
		语句块n+1;
}

注:① switch语句中,break和default不是必须的
  ② switch语句仅支持int类型和String类型数据,但是byte,short,char可以进行自动类型转换
  ③ 如果分支执行了,但是没有break,会出现“case穿透”现象

代码实例:系统接收一个学生的考试成绩,并根据考试成绩输出成绩的等级
/*
 	Switch实例
 	
 	项目要求:
 		1、系统接收一个学生的考试成绩,并根据考试成绩输出成绩的等级
 		2、优[90,100] 
 		   良[80,90)
 		   中[70,80)
 		   及格[60,70)
 		   不及格[0,60)
 		3、成绩可以取小数
 */
public class SwitchTest {
	public static void main(String[] args) {
		java.util.Scanner s = new java.util.Scanner(System.in);
		
		System.out.print("请输入成绩:");
		float chengJi = s.nextFloat();
		int i;
		
		if(chengJi > 100 || chengJi < 0) {
			System.out.println("输入成绩错误!");
		}else {
			i = (int)(chengJi)/10;
			String str = "不及格";
			
			switch(i) {
			case 9:case 10:
				str = "优";
				break;
			case 8:
				str = "良";
				break;
			case 7:
				str = "中";
				break;
			case 6:
				str = "及格";
			}
			System.out.println("该学生的成绩等级为:" + str);
			
		}
		
	}     
	
}

7.2 循环语句

7.2.1 for循环

语法格式
	for (初始化表达式;条件表达式;更新表达式) {
		循环体;
	}

注:① 初始化表达式最先执行,并且在整个循环中只执行一次
  ② 条件表达式的结果必须要是一个boolean类型
  ③ 初始化表达式,更新表达式有时可以缺省

代码实例:for循环实现99乘法表
/*
 	for循环实现99乘法表
 */
public class ForTest {
	public static void main(String[] args) {
		int z;
		for(int x = 1 ; x < 10 ; x++) {
			for(int y = 1 ; y <= x; y++) {
				z = x * y;
				System.out.print(" " + x + "*" + y + "=" + z);
			}
			System.out.println();
		}
	}
}

7.2.2 while循环

语法格式
	while(条件表达式){
		循环体;
	}

注:① 循环体中要注意更新条件变量,防止出现死循环
  ② while循环和for循环差不多

7.2.3 do…while…循环

语法格式
	do{
		循环体;
	} while(条件表达式) ;

注:① 循环体中要注意更新条件变量,防止出现死循环
  ② do…while…循环会先循环一次再进行判断
  ③ while后不要漏掉分号

7.3 转向语句

7.3.1 break语句

注:① break语句用在switch语句中用来终止switch语句的执行(防止case穿透)
  ② 用在循环语句当中,用于终止循环(离break最近的)
  ③ break可以终止指定的循环
    step1 给循环命名
    step2 终止

代码实例:break终止指定的循环
/*
 	break终止指定的循环
 */
public class BreakTest {
	public static void main(String[] args) {
		a:for(int i = 0 ; i < 2 ; i++) { //将for循环标识为a
			for(int j = 0 ; j < 10 ; j++) { //将for循环标识为b
				if(j == 5) {
					break a;
				}
				
				System.out.println("j=" + j);
			}
		}
		
	}
}

7.3.2 continue语句

终止本次循环,直接进入下一次循环

7.3.3 return语句


8、方法

方法是可以完成某个特定功能的并且可以被重复利用的代码片段。

8.1 方法与C语言函数的区别

(1)“函数”是一段实现某种“功能”的代码,函数的操作是对输入数据的处理。
函数的英文function恰恰有“功能”的含义,可以达到“见名知意”。通过函数的名称来实施函数调用。它能将一些数据(也就是参数)传递jQuery进行处理,然后返回一些数据(即函数的返回值),也可以没有返回值。所有传递给函数的数据都是显式传递的。而方法的参数传递通常是隐式的,它可以直接操作类内部的数据。
(2)“方法”也是一段完成某项功能的代码,也通过名字来进行调用,但它依赖于某个特定的对象。
例如,我们可以说“调用对象X的Y方法”,而不能说“调用Y方法”。简单来说,方法和对象相关;而函数和对象无关。
因为Java是一门完全面向对象编程的语言,所以在Java中只有方法。
C语言white面向过程的编程语言,所以在C语言中只有函数。

方法是在类体中定义的

8.2 方法定义的语法机制

[修饰符列表] 返回值类型 方法名 ( 形式参数列表 ) {
	方法体;
}

注:① [ ] 中的内容表示不是必须的,是可选的。
  ② 返回值类型
  返回值可以是仍和Java中合法的类型,如果不需要返回值,则该地方要写void关键字。
  有返回值的话可以使用变量来接收这个返回值,也可以不接收,只不过没有变量接收的话,该返回值的内存会马上释放。
  ③ return语句
  若指定了返回值类型,则必须要写“return 值;” 这句语句,这个语句可以返回方法的执行结果,注意:“return 值;”这个语句执行的可能性要为1。
  只要有return关键字语句的语句执行,当前方法必定结束。因此,在返回值类型为void的方法中,可以用“return;”这个语句来终止方法的执行。
  在同一个域当中,return语句下面不能再编写代码。
  ④ 形式参数列表(形参)
  形式参数列表的每一个参数都是局部变量,方法结束后内存释放。
  形参的个数是0~n个。形参有多个的话使用英文逗号隔开。
  ⑤ 方法体
  方法体由Java语句构成,遵循自上而下的执行顺序。

8.3 方法的调用

语法机制

类名.方法名(实际参数列表);
注:① 实参和形参要一一对应。
  ② 调用本类的方法时,“类名.”可以省略;跨类调用时,“类名.”不能省略。

代码实例:方法的定义以及调用
/*
 	方法的定义以及调用
 */
public class MethodTest01 {
	public static void main(String[] args) {
		int m = 100;
		byte n = 10;
		int result;
		
		//调用本类的方法,没有返回值
		MethodTest01.Sum1(m , n);
		
		//调用本类中的方法可以省略类名
		//使用result变量来接收返回值,并且打印
		result = Sum2(m , n);
		System.out.println(m + "+" + n + "=" + result);
		
		/*错误:The method Method_A() is undefined for the type MethodTest01
		Method_A();*/
		//跨类调用时必须要添加类名
		A.Method_A();
	}

	//定义一个本类中没有返回值的方法
	public static void Sum1(int x, int y) {
		int z = x + y;
		System.out.println(x + "+" + y + "=" + z);
	}
	
	//定义一个本类中有返回值的方法
	public static int Sum2(int x, int y) {
		return x + y;
		//在同一个域当中,return语句下面不能再编写代码,因为这个代码没有执行的机会,无意义
		//错误:Unreachable code
		//System.out.print("abc");
	}
}

class A{
	//定义一个在A类中的方法
	public static void Method_A(){
		System.out.println("This is A' method.");
	}
}

代码实例:用户给输入一个正整数n,输出大于正整数n的最小的质数
/*
 	编写一个程序:用户给输入一个正整数n,输出大于正整数n的最小的质数
 */
public class MethodTest02 {
	public static void main(String[] args) {
		java.util.Scanner s = new java.util.Scanner(System.in);
		System.out.print("请输入一个正整数:");
		int n = s.nextInt();
		
		// 使用for循环寻找n后的最小质数
		for (; !judge(++n) ; ) {	
		}
		System.out.println("输出大于正整数n的最小的质数是:" + n);
	}
	// 判断n是否为质数
	public static boolean judge(int n) {
		for (int m = 2 ; m <= n/2 ; m ++) {
			if (n % m == 0) {
				return false;
			}
		}
		return true;
	}
}

8.4 方法重载机制

作用:简化类似功能方法的引用。
发生方法重载的条件:
① 定义在同一个类中
② 方法名相同
③ 形式参数列表不同(数量不同/顺序不同/类型不同)

代码实例:方法重载机制
/*
 	方法重载机制
 */
public class OverloadTest {
	public static void main(String[] args) {
		M1();
		M1(17 , 18.0F);
		M1(18.0F , 17);
	}
	public static void M1() {
		System.out.println("M1()");
	}
	public static void M1(int x , float y) {
		System.out.println("M1(int x , int y)");
	}
	public static void M1(float x , int y) {
		System.out.println("M1(float x , int y)");
	}
}

8.7 方法递归

注:方法自己调用自己就是方法递归。
  当递归没有结束条件时,会导致栈内存溢出错误。因此,递归必须要有结束条件。
  能用循环实现就尽量不要使用递归。
  发生栈内存溢出错误时的调试思路:
    1.先检查结束条件
    2.若结束条件没有问题,则调大栈的初始内存。

代码实例:使用递归计算1~n的和
/*
 	使用递归计算1~n的和
 */
public class RecursionTest {
	public static void main(String[] args) {
		java.util.Scanner s = new java.util.Scanner(System.in);
		System.out.print("请输入一个正整数:");
		int n = s.nextInt();
		System.out.println(Sum(n));
	}
	
	public static int Sum(int i) { 
		if(i != 1) {
			return i + Sum(i - 1);
		}else {
			return 1;
		}
		
	}
}

8.6 栈数据结构

8.6.1 JVM内存结构

参考资料链接: JVM内存结构.

8.6.2 栈数据结构

栈数据结构


9、面向对象初步

C语言:面向过程
C++语言:一半面向过程,一半面向对象
Java语言:面向对象

9.1 OOA,OOD和OOP

OOA(Object-Oriented Analysis):面向对象分析
OOD(Object Oriented Designing):面向对象设计
OOP(Object Oriented Programming):面向对象编程
整个软件开发过程,都是采用OO贯穿的

9.2面向对象的三个特征

封装、继承、多态

9.3 类和对象

  类中描述的是所有对象的共同特征信息,对象(实例)是通过类创建的个体。就好比水果是一个类,苹果是一个对象。
  类----(实例化)---->对象
  对象----(抽象)----->类
  类=属性+方法

9.3.1 类的定义

语法格式:
	[ 修饰符列表  ] class 类名 {
	类体 = 属性 + 方法;
	}

注:① 修饰符列表可以省略
  ② 属性在代码上以变量的形式存在(成员变量)
  ③ 属性描述状态
  ④ 方法描述动作
  ⑤ 变量必须先声明,再赋值才能访问,但是成员变量没有手动赋值时,系统默认

代码实例:定义一个类

public class Student {
	// 属性描述状态,在Java程序中以“成员变量”的形式存在
	
	String name;//这种成员变量又被称为“实例变量”——对象级别的变量
	String xueHao;
	boolean sex;
	String address;
	int grades;
	// 成员变量没有手动赋值时,系统默认赋值

}

9.3.2 对象的创建和使用

语法格式:
	类名 变量名 = new 类名( );
这里 "new 类名();" 实际上是调用一个系统默认的无参构造方法,在这个方法中会给实例变量赋初始值。详情见
本章节9.6

类是模板,通过一个类,可以创建n个对象。

访问实例变量语法:
	引用.实例变量;

注:① 通过“=”可以修改实例变量的值
  ② 实例变量有一个特点,只要它没有值,系统都会给它赋初始值,除非实例变量已经在所调用的构造方法中赋值了。

代码实例:结合9.3.1的代码,对象的创建和引用
/*
 	对象的创建与使用
 */
public class Object01 {
	public static void main(String[] args) {
		
		// s1这个局部变量叫“引用”,它存储的是对应对象在堆内存中的内存地址
		Student s1 = new Student();
		Student s2 = new Student();
		Student s3 = new Student();
		// Student 是引用数据类型,以上new了3个对象,3个对象都属于学生类型
		// 在堆内存中存储了对象
	}
}

9.3.3 进阶套娃与内存图示意

代码1:地址信息类
/*
 	位置信息
 */
public class Address {
	String city;
	String street;
	String zipcode;
}

代码2:用户信息类
/*
 	用户信息
 */
public class User {
	String userName;
	int age;
	Address addr; //Address类的变量,存储指向Address类型对象的地址
}

代码3:套娃开始
/*
 	套娃开始
 */
public class TaoWa {
	public static void main(String[] args) {
		// new一个Address类型的对象,并将这个对象的地址保存到a变量
		// a称为“引用”,可以通过a来访问对象
		Address a = new Address();
		a.city = "杭州市"; //引用.实例变量; 可以访问实例变量并且改变其值
		a.street = "下沙街道";
		a.zipcode = "310000";
		
		User u = new User();
		u.userName = "张三";
		u.age = 25;
		// 将a存储的Address类型的对象赋值给User类型的对象中的addr
		// 这时addr也指向Address类型的对象,并且可以访问
		// 换言之,addr也是引用
		u.addr = a;
		
		System.out.println("用户名:" + u.userName);
		System.out.println("年龄:" + u.age);
		System.out.println("住址" + u.addr.city + u.addr.street);
	}
}

输出结果:
用户名:张三
年龄:25
住址杭州市下沙街道

内存图示意:
内存图示意

9.4 空指针异常

当引用的内容为空的话,就可能出现空指针异常(NullPointerException)的情况

代码实例:空指针异常
public class NullPointerException {
	public static void main(String[] args) {
		A a = new A();
		a.i = 10;
		System.out.println(a.i);//输出10
		
		//报错:NullPointerException
		a = null;
		System.out.println(a.i);
	}
}

class A {
	int i;
}

9.5 方法调用时值的传递问题

代码实例:
public class Transfer {
	public static void main(String[] args) {
		int i = 10;
		add_1(i);// 将i存储的值复制一份传递给add_1方法的形参i,这两个i的作用域不一样
		System.out.println("main---->" + i);
		
		Age a = new Age();
		a.age = 20;
		add_2(a);// 将a存储的地址复制一份传递给add_2方法的形参a,这两个a的作用域不一样
		//但是这两个a指向的对象都是同一个,堆同一个对象进行操作
		System.out.println("main---->" + a.age);
	}
	
	public static void add_1(int i) {
		i++;
		System.out.println("add_1---->" + i);
	}
	
	public static void add_2(Age a) {
		a.age++;
		System.out.println("add_2---->" + a.age);
	}
}

class Age {
	int age;
}
输出结果:
add_1---->11
main---->10
add_2---->21
main---->21

9.6 构造方法

通过构造方法可以完成对象的创建,以及实例变量的初始化。
当一个类没有提供构造方法时,系统会默认提供一个无参数的构造方法,而这个构造方法被称为缺省构造器。

9.6.1 调用构造方法

使用new运算符来调用构造方法。

语法格式:
	new 构造方法名(实际参数列表);

9.6.2 构造方法的语法格式

[ 修饰符列表 ] 构造方法名 (形式参数列表){
构造方法体;
}

注:① 构造方法名和类名必须一致。
  ② 构造方法不需要指定返回值。
  ③ 通常在构造方法体当中给属性赋值,完成属性的初始化。
  ④ 构造方法支持方法重载。

9.7 实例方法

没有用static关键字修饰的方法。实例方法是对象级别的方法。

9.7.1 如何调用实例方法

实例方法是对象级别的方法,要调用实例方法要先创造一个对象,通过对象去调用。

语法格式:
引用.实例方法名();

9.8 代码实战

代码要求:
定义丈夫类Husband和妻子类wife,
丈夫类的属性包括:身份证号,姓名,出生日期,妻子。
妻子类的属性包括:身份证号,姓名,出生日期,丈夫。
分别给这两个类提供构造方法(无参数构造方法和有参数构造方法都要提供),编写测试程序,创建丈夫对象,
然后再创建妻子对象,丈夫对象关联妻子对象,妻子对象关联丈夫对象,要求能够输出这个"丈夫对象"的妻子的名字,
或者能够输出这个"妻子对象"的丈夫的名字。
并且要求在程序中演示出空指针异常的效果
public class Husband_Wife {
	public static void main(String[] args) {
		Husband h = new Husband("123456798","张三","19970202");
		Wife w = new Wife("147258369","李华","199970317");
		h.wife = w;
		w.husband = h;
		System.out.println(h.name + "妻子的名字是:" + h.wife.name);//丈夫的妻子的名字
		System.out.println(w.name + "丈夫的名字是:" + w.husband.name);//妻子的丈夫的名字
	}
	
}

class Husband{//丈夫类
	//身份证号
	String id;
	//姓名
	String name;
	//出生日期
	String barth;
	//妻子
	Wife wife;
	public Husband() {//无参数构造方法
		
	}
	public Husband(String s1,String s2,String s3) {//有参数构造方法
		id = s1;
		name = s2;
		barth = s3;
	}
}
class Wife{//妻子类
	//身份证号
	String id;
	//姓名
	String name;
	//出生日期
	String barth;
	//丈夫
	Husband husband;
	public Wife() {//无参数构造方法
		
	}
	public Wife(String s1,String s2,String s3) {//有参数构造方法
		id = s1;
		name = s2;
		barth = s3;
	}
}

运行结果:
张三妻子的名字是:李华
李华丈夫的名字是:张三

十、封装

10.1 封装的作用

① 保证内部结构的安全
② 屏蔽复杂,暴露简单
一个类体中的数据如果封装之后,对于代码的调用人员来说,不需要关心其中代码的复杂实现,只需要一个简单的入口就可以访问了。另外,将类体中安全级别较高的数据封装起来,外部人员不能随意访问,就保证了数据的安全。

10.2 get方法和set方法

get方法的规范:
	public 返回值类型 get属性名(无参数){
		return 值;
	}

注:属性名首字符要大写

set方法规范
	public void set属性名(参数){
		属性值=参数;
	}

注:属性名首字符要大写

10.3 封装的步骤

step1 属性私有化
step2 对外提供方法来访问属性,可以在方法中设置关卡来保证数据的安全性。

封装的代码实例:
public class Encapsulation {
	public static void main(String[] args) {
		Student s1 = new Student();
		/*
		 	getAge和setAge均为实例方法,是一个入口
		 	这两个方法都是对象级别的方法
		 	要调用这两个方法要先创建对象,再通过"引用.方法名();"调用
		 */
		System.out.println(s1.getAge());//0
		
		s1.setAge(20);
		System.out.println(s1.getAge());//20
	}

}

class Student {
	private int age;
	
	// setAge方法用来修改age变量的值
	public void setAge(int a) {
		if (a < 0 || a > 150) {//方法入口设立关卡
			System.out.println("输入的年龄不合法");
		}
		age = a;
	}
	
	// getAge方法用来读取age变量的值
	public int getAge() {
		return age;
	}
}
运行结果:
0
20

十一、static和this

11.1 static关键字

static是一个修饰符,用于修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。

11.2 静态变量

加static的变量叫做静态变量是类级别的,静态变量在类加载时初始化,静态变量存储在方法区

11.2.1 什么时候使用静态变量

当该变量是这个类型的所有对象所共有的,且其不随对象的改变而改变时,则其可以声明为静态变量。静态变量在方法区中只保留一份,可以节省内存空间。例如:国籍的中国人类所共有的,且所有对象都是一样的。

11.2.2 访问静态变量

语法格式:
	类名.静态变量;

注:空引用静态变量不会空指针

代码实例:静态变量
/*
 	静态变量
 */
public class StaticTest01 {
	public static void main(String[] args) {
		Chinese c1 = new Chinese("123456789","李四");
		System.out.println(c1.idCard);
		System.out.println(c1.name);
		
		System.out.println(Chinese.nationality);// 静态变量可以通过"类名."访问
		System.out.println(c1.nationality);// 静态变量也可以通过"引用."访问
		
		c1 = null;
		System.out.println(c1.nationality);// 空引用访问静态变量不会空指针错误
		
	}
}
class Chinese{
	String idCard;
	String name;
	static String nationality = "中国";
	
	public Chinese(String s1, String s2) {
		idCard = s1;
		name = s2;
	}
}
运行结果:
123456789
李四
中国
中国
中国

11.3 静态方法

11.3.1 静态方法的调用

语法格式:
	类名.实例方法名(实参列表);

11.3.2 静态方法与实例方法区别

静态方法由static关键字,而实例方法没有。使用静态方法不需要创建对象,使用实例方法需要创建对象。在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都必须依赖具体的对象才能够被调用。但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。

代码实例:静态方法
/*
 	静态方法
 */
public class StaticTest03 {
	public static void main(String[] args) {
		P.print1();//静态方法的调用直接使用"类名.",不需要创建对象
		
		P a = new P();//调用实例方法需要创建对象
		a.print2();//使用"引用."调用实例方法
		
		a.print1();//使用"引用."也可以调用静态方法
		a = null;
		a.print1();//空引用静态方法不会空指针
	}
}
class P {
	String s;
	
	public static void print1() {
		System.out.println("静态方法执行!");
	}
	public void print2() {
		System.out.println("实例方法执行!");
		System.out.println("s的内容为:" + s);
	}
}
运行结果:
静态方法执行!
实例方法执行!
s的内容为:null
静态方法执行!
静态方法执行!

11.4 静态代码块

11.4.1 静态代码块的语法格式

static{
	java语句块;
}

11.4.2 静态代码块特点

在类加载的时候执行,并且只执行一次。在main方法前执行,执行顺序一般是自上而下。一个类当中可以编写多个静态代码块。
Java提供一个了类加载时机,静态代码块可以在类加载时机时执行。

代码实例:静态代码块
/*
 	静态代码块
 */
public class StaticTest02 {
	static {
		System.out.println('A');
	}
	static {
		System.out.println('B');
	}
	
	public static void main(String[] args) {
		System.out.println("main");
	}
	
	static {
		System.out.println('C');
	}
	
	static int i = 100;
	
	static {
		System.out.println("i = " + i);//在静态代码块中可以访问静态变量
	}
}

运行结果:
A
B
C
i = 100
main

11.4.3 对比:实例代码块

语法:
{
	java语句块;
}

实例代码块的特点
Java在构造方法执行之前,会自动执行“实例代码块”中的代码

代码实例:实例代码块
/*
 	实例代码块
 */
public class Instance {
	public static void main(String[] args) {
		new Instance();
	}
	
	public Instance(){
		System.out.println("构造方法执行");
	}
	
	{
		System.out.println("实例代码块执行!");
	}
}

运行结果:
实例代码块执行!
构造方法执行

11.5 this关键字

this是一个变量,是一个引用。this保存当前对象的地址,指向自身。所以,严格意义上来说,this代表的就是“当前对象”。this存储在堆内存当中对象的内部。

11.5.1 this不能省略的情况

在实例方法和构造方法在,用于区分局部变量和实例变量。

代码实例:
/*
 	在实例方法和构造方法在,this用于区分局部变量和实例变量
 */
public class ThisTest01 {
	public static void main(String[] args) {
		Student s1 = new Student();
		s1.setNo("123");
		s1.setName("张三");
		System.out.println("姓名:" + s1.getName());
		System.out.println("学号:" + s1.getNo());
	}
}
class Student{
	//学号
	private String no;
	//姓名
	private String name;
	public Student() {
		
	}
	public Student(String no,String name) {
		this.no = no;
		this.name = name;
	}
	
	public void setNo(String no) {
		this.no = no;//用于区分局部变量no和实例变量no
	}
	public String getNo() {
		return no;
	}
	public void setName(String name) {
		this.name = name;//用于区分局部变量name和实例变量name
	}
	public String getName() {
		return name;
	}
}

运行结果:
姓名:张三
学号:123

11.6 this()语句

语法格式:
	this(实际参数列表);

作用:通过当前的构造方法去调用另一个本类的构造方法,做到代码复用。
注:对this()的调用只能出现在构造方法的第一行。

代码实例:
/*
 	this()语句的使用
 */
public class ThisTest02 {
	public static void main(String[] args) {
		Date d1 = new Date();
		System.out.println(d1.getYear() + "年"  + d1.getMonth() + "月" + d1.getDay() + "日");
	}
}

class Date{
	//年
	private String year;
	//月
	private String month;
	//日
	private String day;
	//无参数构造方法,使得时间的默认值为1970年1月1日
	public Date() {
		this("1970","01","01");
	}
	public Date(String year,String month,String day) {
		this.year = year;
		this.month = month;
		this.day = day;
	}
	//set方法和get方法
	public void setYear(String year) {
		this.year = year;
	}
	public String getYear() {
		return year;
	}
	public void setMonth(String month) {
		this.month = month;
	}
	public String getMonth() {
		return month;
	}
	public void setDay(String day) {
		this.day = day;
	}
	public String getDay() {
		return day;
	}
}

运行结果:
1970年01月01日

十二、继承(extends)

12.1 继承的作用

基本作用:子类继承父类,代码可以得到复用。
主要作用:有了继承关系,才有了方法的覆盖和多态机制。

12.2 继承的语法格式

class 类名 extends 父类名 {
	类体;
}

12.3 继承的特性

① B类继承A类,则称A类为超类(superclass)、父类、基类,B类则称为子类(subclass)、派生类、扩展类。
② java中的继承只支持单继承,不支持多继承,C++中支持多继承,这也是java体现简单性的一点,换句话说,java中不允许这样写代码: class B extends A,C{ }。
③虽然 java中不支持多继承,但有的时候会产生间接继承的效果,例如: class C extends B,class B extends A,也就是说,C直接继承B,其实C还间接继承A。
④ java中规定,子类继承父类,除构造方法不能继承外,剩下都可以继承。但是父类中private数据无法在子类中直接访问,可以间接访问。
⑤ java中的类没有显示的继承任何类,则默认继承Object类,Object类是java语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有Object类型中所有的特征。
⑥ 继承也存在一些缺点,例如:CreditAccount类继承Account类会导致它们之间的耦合度非常高,Account类发生改变之后会马上影响到CreditAccount类。

代码实例:继承

public class ExtendsTest01 {
	public static void main(String[] args) {
		Class_1 s1 = new Class_1();
		s1.setStudentName("李四");
		s1.setStudentNo("147258");
		System.out.println(Student.grade + "年级1班的" + s1.getStudentName() + "同学的学号是:" + s1.getStudentNo());
	}
}
class Student{//学生信息
	private String studentName;
	private String studentNo;
	static int grade = 1;
	public Student() {//无参数构造
		
	}
	public Student(String studentName,String studentNo) {//有参数构造
		this.studentName = studentName;
		this.studentNo = studentNo;
	}
	//set方法和get方法
	public void setStudentName(String studentName) {
		this.studentName = studentName;
	}
	public String getStudentName() {
		return studentName;
	}
	public void setStudentNo(String studentNo) {
		this.studentNo = studentNo;
	}
	public String getStudentNo() {
		return studentNo;
	}
}
class Class_1 extends Student{//班级1的学生
	public Class_1() {//无参数构造
		
	}
}
运行结果:
1年纪1班的李四同学的学号是:147258

十三、方法覆盖和多态

13.1 为什么要进行方法覆盖

子类在继承父类的时候,当继承过来的方法不能满足要求时,子类有权利对这个方法进行重新编写,就是方法覆盖。

13.2 构成方法覆盖的条件

① 两个类必须要有继承关系。
② 重写之后的方法要有与原来的方法相同的返回值,相同的方法名,相同的形式参数列表。

③ 重写之后的方法访问权限不能更低,可以更高。
④ 重写之后的方法不能比之前的方法抛出更多的异常,可以更少。

注:① 方法覆盖只是针对于方法,和属性无关。
② private方法无法覆盖。
③ 构造方法不能被继承,因此也不能覆盖。
④ 方法覆盖只针对实例方法,静态方法覆盖没有意义

代码实例:方法覆盖
/*
 	方法覆盖
 */
public class OverrideTest {
	public static void main(String[] args) {
		Dog d = new Dog();
		d.act();
	}
}
class Animal{
	private String name;
	//构造方法
	public Animal() {
		
	}
	public Animal(String name) {
		this.name = name;
	}
	//set方法和get方法
	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void act() {
		System.out.println("动物在运动");
	}
}
class Dog extends Animal{//方法覆盖只发生在有继承关系的父类和子类的子类中
	public Dog() {
		
	}
	public void act() {//方法覆盖
		System.out.println("小狗在跑");
	}
}

运行结果:
小狗在跑

13.3 多态(polymorphism)

多态是指多种形态,多种状态,编译和运行有两个不同的状态。
编译期叫做静态绑定。运行期叫做动态绑定。
多态的典型代码:父类型的引用指向子类型的对象。

13.4 向上和向下转型

① 向上转型
父类型的引用允许指向子类型的对象。
② 向下转型
父类转换成子类。

注:① 无论是向上转型还是向下转型,两种类型之间必须要有继承关系。
② 需要调用或者执行子类中特有的方法,必须要进行向下转型,才可以调用。
③ 向下转型会出现类型转换异常(java.lang.ClassCastException)

代码实例:向上转型与向下转型
public class PolymorphismTest01 {
	public static void main(String[] args) {
		Animal a1 = new Animal();
		Animal a2 = new Cat(); // 向上类型转换
		Animal a3 = new Bird(); // 向上类型转换
		a1.move();
		a2.move();
		a3.move();
		Cat c1 = (Cat) a2; // 向下类型转换
		c1.catAct(); // 要调用子类特有的方法时,必须要用向下类型转换
		Bird b1 = (Bird) a3;
		b1.birdAct();
	}
}
class Animal {
	public void move() {
		System.out.println("动物在移动");
	}
}
class Cat extends Animal {//Cat类继承Animal类
	public void move() {//方法覆盖
		System.out.println("猫在跑!");
	}
	public void catAct() {//猫类特有的方法
		System.out.println("猫在抓老鼠");
	}
}
class Bird extends Animal {//Bird类继承Animal类
	public void move() {//方法覆盖
		System.out.println("鸟在飞!");
	}
	public void birdAct() {//鸟类特有的方法
		System.out.println("鸟在唱歌");
	}

}
运行结果:
动物在移动
猫在跑!
鸟在飞!
猫在抓老鼠
鸟在唱歌

13.5 类型转换异常(java.lang.ClassCastException)

出现错误的原因:
无论是向上转型还是向下转型,两种类型之间必须要有继承关系。


可以使用instanceof运算符避免

代码实例:ClassCastException
public class ClassCastException {
	public static void main(String[] args) {
		Animal a1 = new Animal();
		Animal a2 = new Cat(); // 向上类型转换
		Animal a3 = new Bird();
		Bird b1 = (Bird) a2;
	}
}
class Animal {
	public void move() {
		System.out.println("动物在移动");
	}
}
class Cat extends Animal {
	public void move() {//方法覆盖
		System.out.println("猫在跑!");
	}
	public void catAct() {//猫类特有的方法
		System.out.println("猫在抓老鼠");
	}
}
class Bird extends Animal {
	public void move() {//方法覆盖
		System.out.println("鸟在飞!");
	}
	public void birdAct() {//鸟类特有的方法
		System.out.println("鸟在唱歌");
	}

}
运行结果:
Exception in thread "main" java.lang.ClassCastException: class Cat cannot be cast to class Bird (Cat and Bird are in unnamed module of loader 'app')
at ClassCastException.main(ClassCastException.java:7)

13.6 instanceof运算符

作用:instanceof运算符可以在运行阶段动态判断引用指向的对象的类型。

instanceof运算符的语法
	(引用 instanceof 类型)

返回结果:instanceof运算符的运算结果只能是true/false

代码实例:使用instanceof运算符避免类型转换异常
public class PolymorphismTest01 {
	public static void main(String[] args) {
		Animal a1 = new Animal();
		Animal a2 = new Cat(); // 向上类型转换
		Animal a3 = new Bird(); // 向上类型转换
		a1.move();
		a2.move();
		a3.move();
		//instanceof运算符的返回值类型只能是true/false
		System.out.println((a2 instanceof Cat));
		System.out.println((a2 instanceof Bird));
		//使用instanceof运算符避免类型转换异常问题
		if (a2 instanceof Cat) {
			Cat c1 = (Cat) a2; // 向下类型转换
			c1.catAct();
		} else if (a2 instanceof Bird) {
			Bird b1 = (Bird) a2;
			b1.birdAct();
		}
		if (a3 instanceof Cat) {
			Cat c1 = (Cat) a3; // 向下类型转换
			c1.catAct();
		} else if (a3 instanceof Bird) {
			Bird b1 = (Bird) a3;
			b1.birdAct();
		}
	}
}
class Animal {
	public void move() {
		System.out.println("动物在移动");
	}
}
class Cat extends Animal {//Cat类继承Animal类
	public void move() {//方法覆盖
		System.out.println("猫在跑!");
	}
	public void catAct() {//猫类特有的方法
		System.out.println("猫在抓老鼠");
	}
}
class Bird extends Animal {//Bird类继承Animal类
	public void move() {//方法覆盖
		System.out.println("鸟在飞!");
	}
	public void birdAct() {//鸟类特有的方法
		System.out.println("鸟在唱歌");
	}

}
运行结果:
动物在移动
猫在跑!
鸟在飞!
true
false
猫在抓老鼠
鸟在唱歌

13.7 多态在开发中的作用

编写一个高层抽象的类,便于扩展。降低程序的耦合度,提高程序的扩展力。


十四、super

14.1 super关键字

super代表的是“当前对象”的那部分父类型特征。

14.2 super注意事项

super应当和this对照起来学习。

super能出现在实例方法和构造方法中
super的语法是:“super.”和“super();”
super不能使用在静态方法当中
“super.”在大部分情况下是可以省略的
“super();”只能出现在构造方法的第一行,通过当前的构造方法去调用父类中的构造方法,目的是:创建子类对象时,先初始化父类特征。

super的使用方法:
super.属性名;
super.方法名(实参列表);
super();

14.3 "super."不能省略的情况

当父类和子类中有一个同名的属性或方法时,想在子类中访问父类的属性或方法,“super.”就不能省略。

代码实例:"super."用于调用父类的属性与方法
/*
 	"super."用于调用父类的属性与方法
 */
public class SuperTest02 {
	public static void main(String[] args) {
		Human h1 = new Student("张三");
		h1.act();
	}
}
class Human{
	String name;
	//构造方法
	public Human() {
		
	}
	public Human(String name) {
		this.name = name;
	}
	//父类的act方法
	public void act() {
		System.out.println("Human类的act方法执行!");
	}
}
class Student extends Human{
	String name;
	//构造方法
	public Student() {
		
	}
	public Student(String name) {
		this.name  = name;
	}
	public void act() {
		System.out.println("Student类的act方法执行!");
		super.act();//调用父类的方法
		System.out.println(name);
		System.out.println(this.name);
		System.out.println(super.name);//输出父类的属性
	}
}
输出结果:
Student类的act方法执行!
Human类的act方法执行!
张三
张三
null

14.4 "super();"的使用

表示通过子类的构造方法去调用父类的构造方法。
注:① 当一个构造方法的第一行既没有“this();”也没有“super();”时,会默认有一个“super();” 因此必须保证父类中要有无参数构造方法。
  ② “this;”和“super;”不能共存,因为他们都出现在构造方法的第一行。

代码实例:"super();"的使用
/*
 	super();
 */
public class SuperTest01 {
	public static void main(String[] args) {
		new C();
	}
}
class A{
	public A() {
		//这里也会默认有super(); 调用老祖宗类Object的无参构造
		System.out.println("A的无参构造");
	}
	public A(int i) {
		
	}
}
class B extends A{
	public B() {
		
	}
	public B(int i) {
		//这里没有写super();和this();
		//默认会有一个super(); 调用父类A中的无参构造
		System.out.println("B的有参构造(int)");
	}
}
class C extends B{
	public C() {
		this(1);// 调用本类中有一个int参数的构造方法
		System.out.println("C的无参构造");
	}
	public C(int i) {
		this(i,1.2);
		System.out.println("C的有参构造(int)");
	}
	public C(int i,double j) {
		super(5);// 调用父类B中的有一个int参数的有参构造
		System.out.println("C的有参构造(int,double)");
	}
}
运行结果:
A的无参构造
B的有参构造(int)
C的有参构造(int,double)
C的有参构造(int)
C的无参构造

14.5 "super(实参);"的使用

调用父类中形式参数列表与传递进"super(实参);"的实际参数列表一致的构造方法

代码实例:"super(实参);"的使用
/*
 	"super(实参);"的使用
 */
public class SuperTest03 {
	public static void main(String[] args) {
		Pet xiaoWnag = new Dog("小汪");
	}
}
class Pet{
	String name;
	//构造方法
	public Pet() {
		
	}
	public Pet(String name) {
		this.name = name;
		System.out.println("Pet类有参构造方法执行!");
	}
}
class Dog extends Pet{
	//构造方法
	public Dog() {
		
	}
	public Dog(String name) {
		super(name);//这里是调用Pet类有一个String类型的参数的构造方法
		System.out.println("Dog类有参构造方法执行!");
	}
}
运行结果:Pet类有参构造方法执行!
Dog类有参构造方法执行!
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值