Java基础笔记
- 注释、标识符、关键字
- 数据类型
- 类型转换
- 变量、常量
注释
- 平时我们编写代码,在代码量比较少的时候,我们还可以看懂自己写的,但是当项目结构一旦复杂起来,我们就需要用到注释了。
- 注释并不会被执行,是给我们写代码的人看的
- 书写注释是一个非常好的习惯
- 平时写代码一定要注意规范。
public class HelloWorld {
public static void main(String [] args){
//这是单行注释
//输出hello,world
System.out.println("hello,world");
//这是多行注释!
/*
这是多行注释
用起来很方便
以后我们用的很多
*/
//javaDoc:文档注释 /** */
/**
* @Description HelloWorld
* @Author 学习Java
*/
}
}
标识符和关键字
关键字
访问控制:
private 私有的
protected 受保护的
public 公共的
类、方法和变量修饰符
abstract 声明抽象
class 类
extends 扩允,继承
final 终极,不可改变的
implements实现
interface 接口
native 本地
new 新,创建
static 静态
strictfp 严格,精准
synchronized 线程,同步
transient 短暂
volatile 易失
程序控制语句
break 跳出循环
continue 继续
return 返回
do 运行
while 循环
if 如果
else 反之
for 循环
instanceof 实例
switch 开关
case 返回开关里的结果
default 默认
错误处理
catch 处理异常
finally 有没有异常都执行
throw 抛出一个异常对象
throws 声明一个异常可能被抛出
try 捕获异常
包相关
import 引入
package 包
基本类型
boolean 布尔型
byte 字节型
char 字符型
double 双精度,
float 浮点
int 整型
long 长整型
short 短整型
变量引用
super 父类,超类
this 本类
void 无返回值
标识符
Java所有的组成部分都需要名字。类名、变量名以及方法名称都被称为标识符
-
所有的标识符都应该以字母(A-Z或者a-z),美元符($),或者下划线(_)开始;"大小写十分敏感“
-
首字母之后可以是字母(A-Z或者a-z),美元符($),或者下划线(_)或者数字的任何字符组合;“特殊符号不能使用”
-
不能使用关键字作为变量名或者方法名。
-
合法标识符举例:age、 $salary、 _value、 _1_value
-
非法标识符举例:123abc、 -salary、 #abc
-
可以使用中文命名,但是一般不建议这样去使用,也不建议使用拼音
数据类型
强类型语言
- 求变量的使用要严格符合规定,所有变量都必须先定以后才能使用 安全性高,但速度慢。
弱类型语言
Java的数据类型分为两大类
public class Demo01 {
public static void main(String[] args) {
//八大基本类型
//整数
int num1 = 10; //常用
byte num2 = 20;
short num3 = 30;
long num4 = 40L; //Long型要在数字后面加个 L
//小数: 浮点数
float num5 = 10.1F; //float类型要在数字后面加个 F 否则会报错
double num6 = 3.234729874923492374728934;
//字符
char name = 'A'; //如果写两个字就会报错 包括 'AB'
String like = "学号Java"; //字符串String不是关键字!切记
//布尔值: 是非
boolean flag = true; //是
boolean flags = false; //非
}
}
-
引用类型(reference type)
- 类
- 接口
- 数组
什么是字节
-
位(bit):是计算机 内部数据 储存的最小单位,11001100是一个八位二进制数。
-
字节(byte):是计算机中 数据处理 的基本单位,习惯上用大写B来表示。
-
1B(byte,字节) = 8bit(位)
-
字符:是指计算机中使用的字母、数字、字和符号
-
1bit表示1位
-
1Byte表示一个字节 1B=8b
-
1024B=1KB
-
1024KB=1M
-
1024M=1G
整数拓展
//整数拓展: 进制 二进制0b 十进制 八进制0 十六进制0x
int i1 = 10;
int i2 = 010; //八进制0
int i3 = 0x10; //十六进制0x
System.out.println(i1); // 10
System.out.println(i2); // 8
System.out.println(i3); // 16
浮点数拓展
//浮点数拓展 问:银行业务怎么表示?--->BigDecimal 数学工具类
//float double 是有限的,离散的,所以他会舍入误差! 大约 接近但是不等于
//最好不要用浮点数进行比较
float f = 0.1f; // 0.1
double d = 1.0/10; // 0.1
System.out.println(f==d); //false
float a = 2387429387489f;
float b = a + 1;
System.out.println(a==b); //true
字符拓展
//字符拓展
char c1 = 'a';
char c2 = '中';
System.out.println(c1); // a
System.out.println((int)c1); //97 强制转换---->所有的字符本质都是数字
System.out.println(c2); //中
System.out.println((int)c2); //20013 强制转换
类型转换
由于Java是强类型语言,所有需要有些运算的时候的,需要用到类型转换。
低 -------------------------------------------------------------->高
byte, short, char -> int -> long -> float -> double
(小数的优先级高于整数)
运算中,不同类型的数据先转换为同一类型,然后进行运算。
强制类型转换 高 - - 低
int i = 128;
byte b = (byte) i; // MAX_VALUE = 127----->MIN_VALUE = -128 强制转换 高--->低
double x = i; //自动转换 低--->高
//内存溢出
System.out.println(i); // 128
System.out.println(b); // -128
System.out.println(x); // 128.0
int money = 10_0000_0000;
int year = 20;
int total1 = money*year; //计算的时候溢出了,-1474836480
long total2 = money*year; //默认是int , 转换之前就已经出现问题了
long total3 = money*((long)year); //先把一个数转换为long
System.out.println(total1); // -1474836480
System.out.println(total2); // -1474836480
System.out.println(total3); // 20000000000
变量、常量、作用域
变量
-
变量是什么:就是可以变化的量!
-
Java是一种强类型语言,每个变量都必须声明其类型。
-
Java变量是程序中最基层的存储单元,其要素包括变量名,变量类型和作用域。
-
注意事项:
-
每个变量都有类型,类型可以是基本类型,也可以是引用类型。
-
变量名必须是合法的标识符。
-
变量声明是一条完整的语句,因此每一个声明都必须以分号结束。
变量作用域
public class Demo04 {
//常量 -->修饰符,不存在先后顺序
//final static double PI = 3.14;
static final double PI = 3.14;
//类变量--static
static double monthSalary = 2500;
//实例变量:从属于对象:如果不自行初始化,输出的就是这个类型的默认值
//默认类型:八大基本类型:--->整形 0 ---->浮点型---->0.0 布尔型 --->false 除了基本类型其余的默认值都是null
String name;
String age;
public static void main(String[] args) {
//变量 :--> 局部变量,类变量,实例变量
//局部变量:必须声明和初始化
int i = 10;
System.out.println(i); // -->10
//类变量--static 可以直接输出 去掉static则会报错
System.out.println(monthSalary); // -->2500.0
//实例变量输出--->变量类型 变量名字 = new demo04();
Demo04 demo04 = new Demo04();
System.out.println(demo04.name); // null
System.out.println(demo04.age); // null
//常量
System.out.println(PI); // 3.14
}
}
常量
- 常量(Constant):初始化(initialize)后不能再改变值!不会变动的值。
- 所谓常量可以理解成一种特殊的变量,他的值被设定后,在程序运行过程中不允许被改变。
final 常量名 = 值;
final double PI = 3.14;
- 常量名一般使用大写字符。
变量的命名规范
-
所有变量、方法、类名:见名知意
-
类成员变量:首字母小写和驼峰原则:monthSalary
-
除了第一个单词外,后面的单词首字母大写
-
局部变量:首字母小写和驼峰原则
-
常量:大写字母和下划线 MAX_VALUE
-
类名:首字母大写和驼峰原则:Man,GoodMan
-
方法名:首字母小写和驼峰原则:run(),runRun()
运算符(基础运算符)
//二元运算符
int a = 10;
int b = 20;
int c = 25;
int d = 25;
System.out.println(a+b); //30
System.out.println(a-b); //-10
System.out.println(a*b); //200
System.out.println(a/b); // 0 --->a/b等于0.5,但是由于没有强转!所以会被忽略小数点后面的数
System.out.println(a/(double)b); //0.5
long e = 1212414394L;
int f = 123;
short g = 10;
byte h = 8;
System.out.println(e+f+g+h); // 1212414535 Long -->多个参数中有一个Long类型,则为Long,没有Long则转为int类型
System.out.println(f+g+h); // 141 int --->有double则结果为double
System.out.println(g+h); // 18 int
//关系运算符 返回结果:正确,错误,布尔值
int x = 10;
int l = 20;
int r = 21;
System.out.println(a>b); // false
System.out.println(a<b); // true
System.out.println(a==b); // false
System.out.println(a!=b); // true
System.out.println(r%x); // 1 --->模运算 取余
自增自减运算符
// ++ -- 自增,自减 一元运算符
int a = 3;
int b = a++; // a++ 的意思是 a = a + 1 --->执行完这行代码后,先给b赋值,再自增
// a = a + 1
System.out.println(a); // 4
// a = a + 1
int c = ++a; // 执行完这行代码前,先自增,再给b赋值
System.out.println(a); //5
System.out.println(b); //3
System.out.println(c); //5
//幂运算 2^3
double pow = Math.pow(2, 3);
System.out.println(pow); // 8.0
逻辑运算符、位运算符
//与(and) 或(or) 非(取反)
boolean a = true;
boolean b = false;
System.out.println("a && b: "+(a&&b)); // false 逻辑与运算:两个变量都为真,结果才为true
System.out.println("a || b: "+(a||b)); // true 逻辑或运算:两个变量有一个为真,结果才为true
System.out.println("!(a && b): "+!(a&&b)); // true 如果为真,则变为假,如果为假,则变为真
//短路运算:逻辑与运算中,如果前面一个参数为假,则不会去执行后面一个参数
int c = 5;
boolean d = (c<4)&&(c++<4);
System.out.println(c); // 5
System.out.println(d); // false
//位运算
/**
* A = 0011 1100
* B = 0000 1101
* ------------------------
* A&B = 0000 1100 两个都为1才为1,反之则为0
* A|B = 0011 1101 两个中有一个为1,就可以为1(包括两个都为1),
* A^B = 0011 0001 异或运算---->相同为0,不相同则为1
* ~B = 1111 0010 取反--->与B相反即可
*
* 面试题:2*8 = 16 ,怎么运算才能最快得出答案
* 左移(>> /2),右移(<< *2),
* 0000 0000 0
* 0000 0001 1
* 0000 0010 2
* 0000 0011 3
* 0000 0100 4
* 0000 1000 8
* 0001 0000 16
*/
System.out.println(2<<3); //16
三元运算符(拓展赋值运算符)
拓展赋值运算符:
//拓展赋值运算符
int a = 10;
int b = 20;
a+=b; // a = a+b 30
a-=b; // a = a-b 10
a*=b; // a = a*b
a/=b; // a = a/b
System.out.println(a);
//字符串连接符 + , 面试题:问两个输出的区别?
System.out.println(""+a+b); // 1020 如果在+运算符两侧出现了String,他就会对加号两侧进行连接
System.out.println(a+b+""); // 30 如果字符串在前面他就会对运算符后面进行拼接,如果字符串在后面,则运算符前面的数会运算
里面拓展了一个面试题:
三元运算符: ? :
//三元运算符 x ? y : z,如果x为真,则结果为y,否则结果为z
int score = 80;
String type = score > 60 ? "及格" : "不及格"; //及格
System.out.println(type);
用户交互Scanner
1、之前我们学的基本语法中我们并没有实现程序和人的交互,但是Java给我们提供了这样一个工具类,我们可以获 取用户的输入。java.util.Scanner 是Java5的新特征,我们可以通过Scanner类来获取用户的输入
2、通过Scanner类的next()与nextLine()方法获取输入的字符串,在读取前我们一般需要使用hasNext()hasNextLine()判断是否还有输入的数据。
1、 next()和hasNext()
//从键盘接收数据
Scanner scanner = new Scanner(System.in);
System.out.println("请输入内容:");
//判断是否还有输入?
if (scanner.hasNext()){
//使用next方式接收
String str = scanner.next();//程序会等待输入内容,不输入会卡在这里
System.out.println("输入的内容为:"+str);
}
//凡是属于IO流的类如果不关闭会一直占用资源.要养成好习惯用完就关掉
scanner.close();
2、 nextLine()和hasNextLine()
Scanner scanner = new Scanner(System.in);
System.out.println("使用nextLine方式接收:");
if (scanner.hasNextLine()){
String str = scanner.nextLine();
System.out.println("输入的内容为:"+ str);
}
scanner.close();
- next():
- 一定要读取到有效字符后才可以结束输入。
- 对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
- 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
- next() 不能得到带有空格的字符串。
- nextLine():
- 以Enter为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符。
- 可以获得空白。
3、 hasNextInt()和hasNextFloat()
Scanner scanner = new Scanner(System.in);
int i = 0;
float f = 0.0f;
System.out.println("请输入整数:");
if (scanner.hasNextInt()){
i = scanner.nextInt();
System.out.println("整数数据为:"+ i);
}else {
System.out.println("输入的不是整数数据!");
}
System.out.println("请输入小数:");
if (scanner.hasNextFloat()){
f = scanner.nextFloat();
System.out.println("小数据为:"+ f);
}else {
System.out.println("输入的不是小数数据!");
}
scanner.close();
4、输入多个数字,并求其总和与平均数
Scanner scanner = new Scanner(System.in);
//和
double sum = 0;
//计算输入了多少个数字
int m = 0;
System.out.println("请输入数字");
while (scanner.hasNextDouble()){
double x = scanner.nextDouble();
m = m + 1; // m++
sum = sum + x;
System.out.println("你输入了第"+m+"个数,然后当前结果sum="+sum);
}
System.out.println(m + "个数的和为:"+sum);
System.out.println(m + "个数的平均值是:"+(sum/m));
scanner.close();
if选择结构
if单选择结构
Scanner scanner = new Scanner(System.in);
System.out.println("请输入内容:");
String s = scanner.nextLine();
//equals:判断字符串是否相等
if (s.equals("Hello")){
System.out.println(s);
}
System.out.println("End");
scanner.close();
equals:判断字符串是否相等
if双选择结构
//考试分数大于60就是及格,小于60就是不及格
Scanner scanner = new Scanner(System.in);
System.out.println("请输入成绩");
int score = scanner.nextInt();
if (score>=60){
System.out.println("成绩及格");
}else {
System.out.println("成绩不及格");
}
scanner.close();
这种方式存在缺陷:只能判断及格和不及格!所以来个多选择结构
if多选择结构
/*
if语句至少有一个else语句,else语句在所有的else if 语句之后。
if语句可以有若干个else if语句,他们必须在else语句之前。
一旦其中一个else if语句检测为true,其他的else if语句以及else语句都将跳过执行
*/
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你的成绩:");
int score = scanner.nextInt();
if (score==100){
System.out.println("你太厉害了,考了一百分!");
}else if (score<100 && score>=90){
System.out.println("A级");
}else if (score<90 && score>=80){
System.out.println("B级");
}else if (score<80 && score>=70){
System.out.println("C级");
}else if (score<70 && score>=60){
System.out.println("D级");
}else if (score<60 && score>=0){
System.out.println("不及格");
}else {
System.out.println("请输入正确的成绩格式!");
}
scanner.close();
嵌套的if结构
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个整数:");
int num1 = sc.nextInt();
System.out.println("请输入第二个整数:");
int num2 = sc.nextInt();
//先判断输入的两个数是否相等,如果不相等又可以分为大于和小于两种情况
if(num1==num2) {
System.out.println(num1+"和"+num2+"相等");
}else {//num1==num2取反,即代表num1!=num2
if(num1>num2) {
System.out.println(num1+"大于"+num2);
}else {
System.out.println(num1+"小于"+num2);
}
}
switch选择结构
- 多选择结构还有一个实现方式就是switch case语句。
- switch case语句判断一个变量与一系列值中的某个值是否相等,每个值称为一个分支。
八大基本类型的选择
//case穿透 switch匹配一个具体的值
char grade = 'D';
switch (grade){
case 'A':
System.out.println("优秀");
break; //如果我们不写break的话!我们的程序即使匹配到了一个具体的值,还是会继续执行下去,这就是case的穿透,所以我们需要加上break
case 'B':
System.out.println("良好");
break;
case 'C':
System.out.println("及格");
break;
case 'D':
System.out.println("再接再厉");
break;
case 'E':
System.out.println("挂科了你");
break;
default:
System.out.println("请输入正确的格式!");
}
字符串类型的选择结构
String name = "重庆";
//我们知道字符的本质还是数字:所以我们可以通过反编译去了解一下为什么可以支持字符串?
//反编译 Java --->.class(字节码文件) --->反编译(IDEA)
//通过反编译可以知道,这里直接将我们name编译成它的hashCode值,然后再去匹配--->每一个对象都有他自己的一个hashCode,通过特定的算法
switch (name){
case "浙江":
System.out.println("发达的浙江");
break;
case "上海":
System.out.println("繁华的上海");
break;
case "重庆":
System.out.println("谢谢你选择了重庆");
break;
case "贵州":
System.out.println("美丽的贵州");
break;
default:
System.out.println("学好java");
}
循环结构
while循环
while是最基本的循环、他的结构为:
- 只要布尔表达式为true,循环就会一直执行下去。
- 我们大多数情况会让循环停止下来,我们需要一个让表达式失效的方式来结束循环。
- 我们正常的业务编程中应该尽量避免死循环。会影响程序性能或者造成程序卡死奔溃!
//计算1+2+3+。。。+100=?
int i = 0;
int sum =0;
while (i<=100){ //需要让表达式失效才能停止循环
sum = sum + i;
i++;
}
System.out.println(sum);
do…while循环
-
对于while语句,如果不满足条件,则不能进入循环。
-
do…while循环和while相似,不同的是,do…while循环至少会执行一次
-
区别:
- while先判断后执行。do while是先执行后判断!
- do…while总是保证循环体会被至少执行一次!,这是他们主要的差别。
int a = 0;
int sum = 0;
do {
sum = sum + a;
a++;
}while (a<=100);
System.out.println(sum);
For循环
我们用三个题来理解for循环
1、计算0~100之间的奇数和偶数的和
int oddSum = 0; //奇数
int evenSum = 0; //偶数
for (int i = 0; i <= 100; i++) {
if (i%2==0){
evenSum+=i;
}else {
oddSum+=i;
}
}
System.out.println("偶数和为:"+evenSum); //偶数和为:2550
System.out.println("奇数和为:"+oddSum); //奇数和为:2500
2、用for循环输出1~1000之间能被5整除的数,并且每行输出3个
for (int i = 0; i < 1000; i++) {
if (i%5==0){
System.out.print(i+"\t");
}
if (i%(3*5)==0){
System.out.println();
}
}
//println 输出完会换行
//print 输出完不会换行
3、打印9*9乘法表
// 第一个数字设为i 第二个数字设为j
// i <= j 去掉重复项
for (int j = 1; j <= 9; j++) {
for (int i = 1; i <= j; i++) {
System.out.print(i+"*"+j+"="+(i*j)+"\t");
}
System.out.println();
}
break、continue
- break可用在任何循环的主体部分,由于强行退出循环,也可以用在switch语句。
int i = 0;
while (i<100){
i++;
System.out.println(i); //30
if (i==30){
break;
}
//System.out.println(i); 29
}
- continue用于循环语句中,终止某次循环过程,跳过剩余语句,之间进行下一次循环条件判断。
int i = 0;
while (i<100){
i++;
if (i%10==0){ //不会执行10的倍数的数,回到程序开头代码,继续执行
System.out.println();
continue;
}
System.out.print(i+" ");
}
打印三角形
要学会打断点
/*
*
***
*****
*******
*********
*/
for (int i = 1; i <= 5; i++) {
for (int j = 5; j >= i; j--) {
System.out.print(" ");
}
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
for (int j = 1; j < i; j++) {
System.out.print("*");
}
System.out.println();
}
java方法详解
Java方法是语句的集合,他们在一起执行一个功能。
-
方法是解决一类问题的步骤的有序结合
-
方法包含于类或对象中
-
方法在程序中被创建,在其他地方被引用
设计方法的原则:方法的本意是代码块,就是实现某个功能的语句块。我们设计方法的时候,最好保持方法的原子性
,就是一个方法只完成1个功能,这样利于我们后期的扩展。
public class Demo01 {
public static void main(String[] args) {
int sum = add(1,4);
System.out.println(sum);
test(); //没有返回值的直接调用,有返回值的传入参数
}
// 注意 void代表没有返回值,但是这里我们需要返回一个a 和 b,所以不加void
// 加上static的话是为了方便我们主方法main调用
public static int add(int a , int b){
return a+b;
}
public static void test() {
for (int i = 0; i < 1000; i++) {
if (i % 5 == 0) {
System.out.print(i + "\t");
}
if (i % (3 * 5) == 0) {
System.out.println();
}
}
}
}
方法的定义
Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段,一班情况下,定义一个方法包含以下语法:
-
方法包含一个方法头和一个方法体。下面是一个方法的所有部分
-
修饰符:修饰符,这是可选的,告诉编译器如何调用该方法,定义了该方法的询问类型
-
返回值类型:方法可能会有返回值,returnValueType是返回值的数据类型,有些方法执行所需的操作,但没有返回值,在这种情况下,returnValueType是关键字void
-
方法名:是方法的实际名称、方法名和参数共同构成方法签名
参数类型参数像是一个占位符,当方法被调用时,传递值给参数,这个值被称为实参或者变量,参数列表是指方法的参数类型、顺序和参数的个数,参数是可选的,方法可以不包含任何参数
public class Demo02 {
public static void main(String[] args) {
int max = sum(10,11);
System.out.println(max+" :比较大哦");
}
public static int sum(int num1,int num2){
int result = 0;
if (num1==num2){
System.out.println("num1==num2");
return 0; //终止方法
}
if (num1>num2){
result = num1;
}else {
result = num2;
}
return result;
}
}
思考:java是值传递还是引用传递?
Java是值传递
面试题:
当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
答:是值传递。Java语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。C++和C#中可以通过传引用或传输出参数来改变传入的参数的值。
方法的重载
重载就是在一个类中,有相同得函数名字,但形参不同的函数
就是一个类里面有两个方法,并且方法的名字相同但是参数列表必须不同
方法重载的规则:
1、方法名称必须相同。
2、参数列表必须不同,(个数不同、类型不同、参数排列顺序不同等)。
3、方法返回值类型可以相同也可以不同。
4、仅仅返回值不同不足以成为方法的重载。
int sum = add(1,4,3);
public static int add(int a , int b){
return a+b;
}
public static int add(int a , int b,int c){
return a+b+c;
}
public static double add(double a , double b){
return a+b;
}
实现理论:方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配、以选择对应的方法的名字,如果匹配失败,则编译器会报错
可变参数
- 在方法声明中,在指定参数类型后加上一个省略号(…)。
- 一个方法中只能指定一个可变参数、它必须是方法的最后一个参数。任何普通的参数必须在它之前声明!
public static void main(String[] args) {
Demo03 demo03 = new Demo03();
demo03.test(3,54,4,56,7); //下面的参数是什么类型上面就传什么类型
}
public void test(int x ,int... i){
}
通过一个简单的排序来理解
public static void main(String[] args) {
//调用可变参数的方法
printMax(34,4,5,1,34.4);
printMax(new double[]{3,4,2});
}
public static void printMax(double... numbers){
if (numbers.length == 0){
System.out.println("no argument passed");
return;
}
double result = numbers[0];
//排序!
for (int i = 0 ; i <numbers.length; i++){
if (numbers[i] > result){
result = numbers[i];
}
}
System.out.println("the max value is :" + result);
}
递归
就是方法自己调用自己。(用栈实现,故仅建议在基数较小时使用)
递归包含两部分:
-
递归头:什么时候不调用自身方法,如果没有头,将陷入死循环。(边界条件)
-
递归体:什么时候需要调用自身方法。
public static void main(String[] args) {
System.out.println(f(4));
}
public static int f(int n){
if (n==1){
return 1;
}else {
return n*f(n-1);
}
}
数组
数组的声明和创建
1、什么是数组:数组是相同类型的数据的有序集合。描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成,其中每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。
2、 数组的声明创建:数组必须先声明后使用,数组有两种声明方式:
int[] nums;//声明一个数组
nums = new int[5]; //创建一个数组
//给数组元素赋值
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
System.out.println(nums[3]); //打印数组下标为3的数
//计算所有元素的和
int sum = 0;
//获取数组长度: array.length
for (int i = 0; i < nums.length; i++) {
sum = sum + nums[i];
}
System.out.println(sum); //15
三种初始化及内存分析
静态初始化
在声明时直接赋值。
//静态初始化
int[] a = {1,23,45,6,7};
System.out.println(a[3]); // 6
动态初始化
动态初始化:就是先声明创建,之后再赋值,包含了数组的默认初始化。
//动态初始化: 包含默认初始话
int[] b = new int[5];
b[0] = 2;
b[1] = 3;
System.out.println(b[0]); // 2
System.out.println(b[1]); // 3
数组的默认初始化:
数组时引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
数组的四个基本特点
1、其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
2、其元素必须是相同类型,不允许出现混合类型。
3、数组中的元素可以是任何数据类型,包括基本类型和引用类型。
4、数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
数组的使用
For-Each循环
数组作方法入参
数组作返回值
public static void main(String[] args) {
int[] arrays = {1,2,3,4,5,9};
//foreach打印数组 ,没有下标
for (int array : arrays) {
System.out.println(array);
}
System.out.println("=================");
//printArray(arrays);
int[] reverse = reverse(arrays);
printArray(reverse);
}
//数组长度就是你数组中有几个数
public static void printArray(int[] array){
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
}
//反转数组
public static int[] reverse(int[] array){
int[] result = new int[array.length];
//反转的操作
for (int i = 0,j= result.length-1; i < result.length; i++,j--) {
result[j] = array[i];
}
return result;
}
二维数组
多维数组:可以看做是数组的数组,如:二维数组的每一个元素都是一个一维数组
/*
1,2 array[0]
2,3 array[1]
3,4 array[2]
4,5 array[3]
*/
int[][] array = {{1,2},{2,3},{3,4},{4,5}};
System.out.println(array.length); //4
System.out.println(array[0].length); //2
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.print(array[i][j]+" "); //1 2 ,2 3, 3 4, 4 5
}
}
Array类讲解
数组的工具类java.util.Arrays,其中的方法都有static修饰,可以直接使用类名进行调用,不必使用对象来调用。
常用功能:
-
对数组赋值:通过fill方法。
-
对数组排序:通过sort方法,按升序。(共八种)
-
比较数组:通过equals方法比较数组元素值是否相等。
-
查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找法操作
int[] a = {2,5,9,34,22,675,342,22};
System.out.println(a); //[I@6e8dacdf ,对象a的hashcode值
//打印数组元素
System.out.println(Arrays.toString(a)); //[2, 5, 9, 34, 22, 675, 342, 22]
//数组进行排序:升序
Arrays.sort(a);
System.out.println(Arrays.toString(a)); //[2, 5, 9, 22, 22, 34, 342, 675]
//数组填充
Arrays.fill(a,0);
Arrays.fill(a,2,4,0); //下标为2和4的数被0填充
System.out.println(Arrays.toString(a)); //[0, 0, 0, 0, 0, 0, 0, 0]
System.out.println(Arrays.toString(a)); //[2, 5, 0, 0, 22, 34, 342, 675]
冒泡排序
public static void main(String[] args) {
int[] a = {2,35,43,53,546,67858,45};
int[] sort = sort(a);
System.out.println(Arrays.toString(sort));
}
public static int[] sort(int[] array){
//临时变量
int temp = 0;
//外层循环,判断我们这个要走多少次;
for (int i = 0; i < array.length-1; i++) {
boolean flag = false;
//内层循环,比较判断两个数,如果第一个数比第二个数大,则交换位置
for (int j = 0; j < array.length-1-i; j++) {
if (array[j+1]<array[j]){
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag = true;
}
}
if (flag==false){
break;
}
}
return array;
}
面向对象
回顾方法调用
我们定义两个类Teacher和Student然后在Student中定义一个say()方法,然后在Teacher中调用say方法
Teacher
public class Teacher {
public static void main(String[] args) {
Student.say();
}
}
Student
public class Student {
public static void say(){
System.out.println("学生说话了");
}
}
可以直接通过类名.方法调用,然后我们去掉static之后发现不能调用了,这时的Student变成了非静态方法,我们需要在Teacher中通过new()一个Student对象来调用他的方法
public class Student {
//非静态方法
public void say(){
System.out.println("学生说话了");
}
}
public class Teacher {
public static void main(String[] args) {
//不是静态方法以后,我们需要实例化这个类 new
//对象类型 对象名 = 对象值
Student student = new Student();
student.say();
}
}
我们再到Teacher中定义两个方法a()和b()
//两个都是非静态的方法之间可以调用
public void a(){
b();
}
public void b(){
a();
}
//两个方法都是静态的,方法之间可以调用
public static void a(){
b();
}
public static void b(){
a();
}
//a()是静态方法,b()是非静态方法
public static void a(){
//发现在a()中不能调b
}
public void b(){
//在b()中可以调a()
a();
}
也就是说,同一个类中,非静态方法之间可以相互调用,两个都是静态方法之间可以调用,静态方法不能调非静态方法,非静态方法可以调静态方法,static是和类一起加载的,类存在就已经存在了,非静态方法是类实例化之后才存在的,一个已经存在的东西去调一个还不存在的东西,所以会报错
通过代码理解值传递
public class Demo01 {
public static void main(String[] args) {
int a = 1;
System.out.println(a); // 1
Demo01.change(a);
System.out.println(a); // 1
}
//返回值为空
public static void change(int a){
a = 10;
}
}
类与对象的创建
面向对象的本质就是:以类的方式组织代码,以对象的组织(封装)数据
三大特性:封装,继承,多态
认识论的角度考虑先有对象后有类,对象是具体的事物;类似对对象的抽象,是一种抽象的数据定义,是对某一类事物整体的描述/定义,不是具体的事务
代码运行的角度先有类后有对象,类似对象的模板
定义两个类,Father 和 Mather
Father
//一个类里面只能有属性和方法
public class Father {
//属性
String name;
int age;
//方法
public void student(){
System.out.println(this.name+"在学习!");
}
}
Mather
public class Mather {
public static void main(String[] args) {
//类: 抽象的,实例化 就像我们的父亲,是古人这么叫下来的,所以我们跟着叫
//类实例话以后会返回一个自己的对象China
//China对象就是一个Father的具体实例
Father china = new Father();
//贵州也是我的父亲
Father guiZhou = new Father();
china.name="wansui";
china.age=1;
System.out.println(china.age);
System.out.println(china.name);
}
}
完成对象之间的调用和传参,赋值,输出,以及对象的理解
构造器详解
-
使用 new 关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
-
类中的构造器也称为构造方法,是在进行创建对象的时候必须调用的。并且构造器有以下两个特点:
1、必须和类的名字相同
2、必须没有返回类型,也不能写 void
创建一个Person类:
public class Person {
//一个类即使什么都不写,它也会存在一个方法--->无参构造方法
//显示的定义构造器
String name;
int age;
//无参构造
public Person(){}
//有参构造:一旦定义了有参构造,无参构造就必须显示的定义
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
/*
构造器:
1、和类名相同
2、没有返回值
作用:
1、new 本质在调用构造方法
2、初始化对象的值
注意点:
1、定义有参构造之后,如果想使用无参构造,显示的定义一个无参的构造
*/
创建一个Application类:
public class Application {
public static void main(String[] args) {
//new就是实例化了一个对象
Person person1 = new Person(); //不传参数就是走无参构造
Person person2 = new Person("重庆",99); //传参数就是走有参构造
System.out.println(person1.name); // null
System.out.println(person2.name); // 重庆
}
}
创建对象内存分析
Pet
public class Pet {
//属性
public String name;
public int age;
//方法
public void shout(){
System.out.println("叫了一声!");
}
}
Pets
public class Pets {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "小黑";
dog.age = 3;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age);
}
}
1、栈内存中放哪些东西?
①基本类型的变量,例如int age=3中的age;
②对象的引用变量,例如Thread boss=new Thread();中的 boss。
当在代码块中定义一个变量时,Java就在栈中为这个变量分配内存空间;当超过变量的作用域后,Java会自动 释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。
2、堆内存中存放哪些东西?
① 存放由new创建的对象和数组。
在堆中存放的内存,由Java虚拟机垃圾回收器来管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量持有的内容等于数组或者对象在堆内存中的首地址。在栈中的这个特殊的变量,就成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号。
3、静态区/方法区:
方法区(method)也叫做静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
方法区中包含的都是在整个程序中永远唯一的元素,例如class,static变量。
全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量放在相邻的另一块区域
面向对象的三大特性
封装
-
改露的露,该藏的藏
- 程序设计要求追求**“高内聚,低耦合”**。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合就是仅暴露少量的方法给外部使用。
-
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表现,而应通过操作接口来访问,这称为信息隐藏
-
记住 属性私有,get/set,只能通过开放的方法 get/set 对类里的私有属性进行有限操作。
public class Study1 {
//private属性私有化,就是不让其他类直接调用
private String name;
private int id;
private char sex;
private int age;
//我们通过get/set让其他类来调用我们的属性;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>120 || age<0){
this.age=3;
}else {
this.age=age;
}
}
}
当我们和往常一样去调用这个类里面的方法的时候,我们会发现调用不了了
public class StudyImpl {
public static void main(String[] args) {
Study1 study1 = new Study1();
//study1.name; 发现没有public只有不能直接调用
study1.setName("重庆");
System.out.println(study1.getName()); // 重庆
study1.setAge(666);
System.out.println(study1.getAge()); // 3
}
}
/*
1.提高程序的安全性,保护数据
2.隐藏代码的实现细节
3.统一接口
4.系统可维护增加了
*/
类的封装,就是将类中的属性私有化,即用private关键字来修饰。私有属性只能在它所在的类中被访问。
如果外界想要访问私有属性,需要提供一些使用private修饰的公有方法。其中包括用于获取属性值的getXxx方法和设置属性值的setXxx方法 。
在Java中,针对类、成员方法和属性提供了4种访问级别,分别是
private、default、protected、public
-
Private(类访问级别):如果类的成员被private访问控制符来修饰,则这个成员只能被该类的其他成员访问,其他类无法直接访问。类的良好封装就是通过private关键字来实现的。
-
Default (包访问级别):如果一个类或者类的成员不使用任何访问控制符修饰, 则称它为默认访问控制级别,这个类或者类的成员只能被本包中的其他类访问。
-
protected (子类访问级别):如果—个类的成员被protected访问控制符修饰, 那么这个成员既能被同一包下的其他类访问, 也能被不同包下该类的子类访问。
-
Public (公共访问级别):这是一个最宽松的访问控制级别, 如果—个类或者类的成员被public访问控制符修饰,那么这个类或者类的成员能被所有的类访问,不管访问类与被访问类是 否在同—个包中。
权限的四个优先级由低到高:public–>protected–>default–>private
继承(extends)
-
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。extends 的意思是“扩展”,子类是父类的扩展。Java中类只有单继承,没有多继承!
-
继承是类和类之间的一种关系,除此之外,类和类之间的关系还有依赖、组合、聚合等。继承关系的两个类,一个为子类(派生类),一个为父类(基类)。
-
子类继承父类,使用关键字 extends 来表示。子类和父类之间,从意义上讲应该具有“is a”的关系。
定义一个父类Person
//Person 父类或者基类
public class Person {
private int money = 10_0000_0000;
public void say(){
System.out.println("说了一句话!");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
然后我们定义一个子类Student去继承Person
//继承了Person,就是子类,派生类,拥有了父类所有的东西
public class Student extends Person{
}
然后我们里面什么都不写,我们去Application这个类里面去new一个这个子类
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();
student.setMoney(100);
System.out.println(student.getMoney());
}
}
发现我们明明什么都没有写,却可以用到一个方法和属性!这就是继承
当然,在java中,所有的类,都默认直接或者间接继承Object
Object中有几个方法,所有的类都可以用,
super详解
对比 this 指针是指向当前类,super 指向当前类的父类,从而可以访问父类中的一些开放的属性及方法。
Person(父类)
public class Person {
//无参构造
public Person() {
System.out.println("Person无参构造执行力");
}
//把修饰词换成private后在Student中就不能访问父类属性
protected String name = "chongqing";
//私有的东西不能被继承!private
public void print(){
System.out.println("Person");
}
}
Student子类
public class Student extends Person {
//无参构造
public Student() {
//如果父类有有参构造,但是没有显示的定义无参构造,那么子类也无法显示的定义无参构造
//隐藏代码:调用了父类的无参构造
super();//调用父类的构造器,必须要在子类构造器的第一行、this也是一个道理
System.out.println("Student无参构造执行力");
}
private String name = "chong";
public void print() {
System.out.println("Student");
}
public void test1() {
print(); //Student
this.print(); //Student
super.print(); //Person
}
public void test(String name){
System.out.println(name); //qing
System.out.println(this.name); //chong
System.out.println(super.name); //chongqing
}
}
测试类Application
public class Application {
public static void main(String[] args) {
Student student = new Student();
//student.test("qing");
//student.test1();
}
}
子类对象构造函数默认先调用父类构造函数
super小结:
- super 调用父类的构造方法,必须在构造方法的第一个
- super 必须只能出现在子类的方法或者构造方法中
- super 和 this 不能同时出现在构造方法中(两者都得在第一行)
对比this
代表的对象不同:
this 指向本身调用者这个对象;super 指向父类对象的引用
前提不同:
this 没有用继承也可以使用;super 只能在继承条件下才可以使用
构造方法不同:
this 调用本类的构造方法;super 调用父类的构造方法
方法重写
重写都是方法重写与属性无关,重写方法与静态方法还是非静态方法相关
静态方法
B:
//重写都是方法的重写,和属性无关
public class B {
public static void test(){
System.out.println("B==>test()");
}
}
A(A继承了B)
public class A extends B{
//Override重写
//@Override//注解;有功能的注解
public static void test() {
System.out.println("A==>test()");
}
}
测试B调用test()A调用test()分别输出了什么?
public class Application {
public static void main(String[] args) {
//方法的调用只和左边定义的数据类型有关
A a = new A();
a.test(); // A==>test()
B b = new A();
b.test(); // B==>test()
}
}
非静态方法:
//重写都是方法的重写,和属性无关
public class B {
public void test(){
System.out.println("B==>test()");
}
}
public class A extends B{
//Override重写
@Override//注解;有功能的注解
public void test() {
System.out.println("A==>test()");
}
}
public class Application {
public static void main(String[] args) {
//方法的调用只和左边定义的数据类型有关
A a = new A();
a.test(); // A==>test()
//父类的引用指向子类
B b = new A();
b.test(); // A==>test()
}
}
重写小结:前提是有继承关系,而且必须是子类重写父类方法!
-
方法名必须相同
-
参数列表也相同(否则就是重载了)
-
修饰符:范围可以扩大;可以从父类的 private 方法扩到子类的 public 方法。 public–>protected–>default–>private
-
抛出的异常:范围可以缩小但不能扩大。
重写,子类的方法名必须和父类的保持一致,方法体不同
多态
即同一方法可以根据发送对象的不同而采用多种不同的行为方式。一个对象的实际类型是确定的,但可以指向对象的引用类型有很多
多态存在的条件:有继承关系,子类重写父类方法,父类引用指向子类对象
注意:多态是方法的多态,属性没有多态
Person:父类
public class Person {
public void run(){
System.out.println("run");
}
}
Student:子类
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
测试类Test:
public class Test {
public static void main(String[] args) {
//一个对象的实际类型是确定的
//可以指向的引用类型就不确定了:父类的引用指向子类
//Student能调用的方法都是自己的或者是继承父类的!
Student s1 = new Student();
//Person父类型,可以指向子类,但是不能调用子类独有的方法!
Person s2 = new Student();
Object s3 = new Student();
//子类重写了父类的方法,如果都调用,则走子类的方法
s1.run(); //son
s2.run(); //son
//对象能执行哪些方法,主要看对象左边的类型,和右边关系不大!
s1.eat();
((Student) s2).eat(); //父类没有的方法,需要强转
}
}
多态的注意事项:
- 多态是方法的多态,属性没有多态
- 父类和子类因为有联系才有多态,否则就是类型转换异常 ClassCastException!
- 存在的条件:有继承关系,且方法需要重写,父类引用指向子类对象 Father f1 = new Son();
哪些方法不能被重写?
1、static方法,属于类,它不属于实例
2、被final修饰的方法
3、private修饰的方法
static
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(); //在主方法中可以直接调用静态方法
Student.go(); // 也可以直接通过类名.方法 调用
//run(); 无法调用非静态方法
}
}
public class Person {
{
System.out.println("匿名代码块");
}
//输出顺序为:1.静态代码块 2.匿名代码块 3.无参构造方法
//static只执行一个
static {
System.out.println("静态代码块");
}
public Person(){
System.out.println("无参构造方法");
}
public static void main(String[] args) {
Person person1 = new Person();
System.out.println("==================");
Person person2 = new Person();
}
}
//输出效果
/*
静态代码块
匿名代码块
无参构造方法
==================
匿名代码块
无参构造方法
*/
抽象类
abstract 修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。抽象类中可以没有抽象方法,但是抽象方法的类一定要声明为抽象类。
//abstract 抽象类
public abstract class Action {
//抽象类就是一个约束,有人帮我们实现~
//abstract ,抽象方法,只有方法名字,没有方法的实现!
public abstract void doSomething();
//1.不能new这个抽象类,只能靠子类去实现它;这是一种约束!
//2.抽象类中可以写普通的方法
//3.抽象方法必须在抽象类中
}
继承Action
// 抽象类的所有方法,继承了它的子类,都必须要实现它的方法;除非它也是抽象类
// public abstract class ActionB extends Action
public abstract class ActionB extends Action{
@Override
public void doSomething() {
System.out.println("doSomething");
}
}
接口
-
接口:只有规范!自己无法写方法~专业的约束!实现了约束与实现分离:面向接口编程
-
声明类的关键字是 class,声明接口的关键字是 interface
-
接口的本质是契约,就像我们人之间的法律,制定好后大家都遵守
//interface 定义的关键字,接口都需要有实现类
public interface UserService {
//接口中也可以定义变量~public static final
int AGE = 99;
//接口中的所有定义其实都是抽象的 public abstract
void add(String name);
void delete(String name);
void query(String name);
}
实现类
//抽象类用的是:extends
//类可以实现接口用 implements
//实现了接口的类,就需要重写接口中的方法~
//利用我们的接口就变相的实现了多继承
public class UserServiceImpl implements UserService{
@Override
public void add(String name) {}
@Override
public void delete(String name) {}
@Override
public void query(String name) {}
}
接口小结:接口是
1.约束
2.定义一些方法,让不同的人实现
3.接口中方法默认有关键字 public abstract
4.接口中属性默认有关键字 public static final
5.接口不能被实例化,接口中也没有构造方法
6.implements 可以实现多个接口
7.实现类必须重写接口中的方法
异常
要理解 Java 异常处理是如何工作的,你需要掌握以下三种类型的异常:
- 检查性异常:最具代表性的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误 ERROR:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到。
-
在 Exception 分支中有一个重要的子类 RuntimeException(运行时异常)
ArrayIndexOutOfBoundException(数组下标越界)
NullPointerException(空指针异常)
ArithmeticException(算术异常)
MissingResourceException(丢失资源)
ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理
-
这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;
-
Error 和 Exception 的区别:Error 通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception 通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
异常结构图
捕获和抛出异常
异常处理五个关键字:try, catch, finally, throw, throws
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
try { //try监控区域
System.out.println(a/b);
} catch (Exception e) { //捕获异常
System.out.println("分母不能为0");
} finally {
System.out.println("finally");
}
}
}
异常大小在catch中的位置,捕获多个异常
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
try { //try监控区域
//异常的大小必须由上自下变大,如果第一个就写Throwable,那代码就不会继续向下走了
System.out.println(a/b);
} catch (Error e) { //捕获异常
System.out.println("Error");
} catch (Exception e) { //捕获异常
System.out.println("Exception");
}catch (Throwable e) { //捕获异常
System.out.println("Throwable");
} finally {
System.out.println("finally");//可以不要finally,代码块可以用来关闭资源
}
}
}
throw和throws
public class Demo01 {
public static void main(String[] args) {
try {
new Demo01().test(1,0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
}
public void test(int a , int b)throws ArithmeticException{ //注意throws和throw的区别
if (b==0){
throw new ArithmeticException(); //主动抛出异常,一般用在方法中
}
}
}
太在意从前,又太恐惧将来,昨天是段历史,明天是个谜团,而今天是天赐的礼物,要像珍惜礼物,那样珍惜今天 加油!