JAVA学习笔记

一、 第一个JAVA程序

1.Java对大小写敏感,如果出现了大小写拼写错误,程序无法运行。
2.关键字public被称作访问修饰符(access modifier),用于控制程序的其它部分对这段代码的访问级别。
3.关键字class 表明Java 程序中的全部内容都包含在类中,Java是一种纯面向对象的语言。
4.一个源文件中至多只能有一个public的类声明,其它类的个数不限,如果源文件中包含一个public 类,源文件名必须和它中定义的public的类名相同,且以“java”为扩展名。
5.一个源文件可以有多个class
6.正确编译后的源文件,会得到相应的字节码文件,编译器为每个类生成独立的字节码文件,且将字节码文件自动命名为类的名字且以“class”为扩展名。
7.main方法是Java应用程序的入口方法,它有固定的书写格式:

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

8.在Java中,用花括号划分程序的各个部分,任何方法的代码都必须以“{”开始,以“}”结束,由于编译器忽略空格,所以花括号风格不受限制。
9.Java中每个语句必须以分号结束,回车不是语句的结束标志,所以一个语句可以跨多行。
10.编程风格:

  1. 注意缩进!
  2. 成对编程!

二、注释

1、注释就是程序员为读者作的说明,是提高程序可读性的一种手段
2、在Java中,有2种表示注释的方法

// 单行注释----注释内容从//到本行结尾
/*
单行或多行注释
/
/
*/注释不能嵌套

3、Java中的注释不会出现在字节码文件中。即JAVA编译器编译时会跳过注释语句。

三、命名规范

1、代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束

反例:_name / __name / O b j e c t / n a m e / n a m e Object / name_ / name Object/name/name / Object$

标识符由字母、下划线、$和数字组成,长度不受限制、第一个字符不能是数字字符
2、代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式,即使纯拼音也要避免采用。

正例:alibaba / taobao / youku / hangzhou 等国际通用的名称,可视同英文
反例:DaZhePromotion [打折] / getPingfenByName() [评分] / int 某变量=3

3、类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DTO / VO / AO

正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromot
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromot

4、方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCas驼峰形式。

正例: localValue / getHttpMessage() / inputUserId

5、常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚

正例:MAX_STOCK_COUNT
反例:MAX_COUNT

6、抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Excepti命名以它要测试的类的名称开始,以 Test 结尾。
7、中括号是数组类型的一部分,数组定义如下:String[] args;
反例:使用 String args[]的方式来定义。
8、POJO 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。

反例:定义为基本数据类型 Boolean isDeleted的属性,它的方法也是isDeleted(),RPC框架在反向解析的时候,“以为”对应的属性名称是deleted,导致属性获取不到,进而抛出异常。

9、包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。

正例: 应用工具类包名为 com.alibaba.open.util
类名为 MessageUtils

10、杜绝完全不规范的缩写,避免望文不知义。

反例:AbstractClass“缩写”命名成 AbsClass;condition“缩写”命名成condi

,此类随意缩写严重降低了代码的可阅读性。
11、为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词组合来表达其意。

正例:从远程仓库拉取代码的类命名为 PullCodeFromRemoteRepository。
反例:变量 int a; 的随意命名方式。

12、如果模块、接口、类、方法使用了设计模式,在命名时体现出具体说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。

正例:public class OrderFactory;
public class LoginProxy; public class
ResourceObserver;

13、接口类中的方法和属性不要加任何修饰符号(public 也不要加)保持代码的简洁性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。

正例:接口方法签名:void f();
接口基础常量表示:String COMPANY = “alibaba”;
反例:接口方法定义:public abstract void f();

说明:JDK8 中接口允许有默认实现,那么这个 default 方法,是对所有实现认实现。
14、接口和实现类的命名有两套规则
1)对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。
正例:CacheServiceImpl 实现 CacheService 接口。
2)如果是形容能力的接口名称,取对应的形容词做接口名(通常是-able的形式)

正例:AbstractTranslator 实现 Translatable。

15、枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。
说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。
正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKOWN_R
16、各层命名规约:
A) Service/DAO 层方法命名规约

  • 1) 获取单个对象的方法用 get 做前缀。
  • 2) 获取多个对象的方法用 list 做前缀。
  • 3) 获取统计值的方法用 count做前缀。
  • 4) 插入的方法用 save/insert 做前缀。
  • 5) 删除的方法用 remove/delete 做前缀
  • 6) 修改的方法用 update 做前缀。

B) 领域模型命名规约

  • 1) 数据对象:xxxDO,xxx 即为数据表名。
  • 2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
  • 3)展示对象:xxxVO,xxx 一般为网页名称。
  • 4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJ

四、整型变量

十进制转换为其他进制:

Integer.toBinaryString(a) //转换为二进制
Integer.toOctalString(a) //转换为八进制
Integer.toHexString(a) //转换为十六进制

五、浮点型

1、float类型又被称作单精度类型,尾数可以精确到7位有效数字,在很多情况下,float类型的精度很难满足需求。
2、double表示这种类型的数值精度是float类型的两倍,又被称作双精度,绝大部分应用程序都采用double类型。
3、Java 浮点类型常量有两种表示形式
4、十进制数形式,例如:

3.14 314.0 0.314

5、科学记数法形式,如

double f = 314e2; //31410^2–>31400.0
double f2 = 314e-2; //314
10^(-2)–>3.14

6、Float类型的数值有一个后缀F/f ,没有后缀F/f的浮点数值默认为double类型。也可以在浮点数值后添加后缀D/d, 以明确其为double类型
7、浮点类型float, double的数据不适合在不容许舍入误差的金融计算领域。
如果需要进行不产生舍入误差的精确数字计算,需要使用BigDecimal类。
注:
主要理由:由于字长有限,浮点数能够精确表示的数是有限的,因而也是离散的。浮点数一般都存在舍入误差,很多数字无法精确表示(例如0.1),其结果只能是接近, 但不等于。

二进制浮点数不能精确的表示0.1,0.01,0.001这样10的负次幂。并不是所有的小数都能可以精确的用二进制浮点数表示。
最好完全避免使用浮点数比较:

        float f = 0.1f;
		double d = 1.0/10;
		System.out.println(f==d);   //false


		float d1 = 423432423f;
		float d2 = d1+1;
		if(d1==d2){
			System.out.println("d1==d2");
		}else{
			System.out.println("d1!=d2");
		}

8、大数值:
Java.math下面的两个有用的类:BigInteger和BigDecimal,这两个类可以处理任意长度的数值。BigInteger实现了任意精度的整数运算。BigDecimal实现了任意精度的浮点运算。
9、浮点数使用总结:
1.默认是double
2.浮点数存在舍入误差,很多数字不能精确表示。如果需要进行不产生舍入误差的精确数字计算,需要使用BigDecimal类。
3.避免比较中使用浮点数

六、字符型(2个字节)

1、单引号用来表示字符常量。例如:‘A’是一个字符,而“A”则是一个字符串。
2、char类型用来表示在Unicode编码表中的字符,(注:Unicode编码被设计用来处理各种语言的所有文字,它占2个字节,可允许有65536个字符;ASCII码占一个字节,可允许有128个字符,是Unicode编码表中前128个字符)
3、 Unicode具有从0到65535之间的编码,他们通常用从’\u0000’到’\uFFFF’之间的十六进制值来表示(前缀为u表示Unicode)

char c = ‘\u0061;

Java 语言中还允许使用转义字符 ‘\’ 来将其后的字符转变为其它的含义,

char c2 = ‘\n’; //代表换行符

转义符 含义 Unicode值

\b 退格(backspace)\u0008
\n 换行 \u000a
\r 回车 \u000d
\t 制表符(tab) \u0009
\“ 双引号 \u0022
\‘ 单引号 \u0027
\ 反斜杠 \u005c

注:以后我们学的String类,其实是字符序列(char sequence)。
4、boolean类型(一位,不是一个字节)
Boolean类型有两个值,true和flase,不可以0或非0的整数代替,与C不同,一般用来判断逻辑条件。

七、自动类型转换

自动类型转换:容量小的数据类型可以自动转换为容量大的数据类型。
特例: 可以将整型常量直接赋值给byte, short, char等类型变量,而不需要进行强制类型转换,只要不超出其表述范围。

Short b = 12; //合法
short b = 1234567; //非法

八、强制类型转化

强制类型转换,又被称为造型,用于显式的转换一个数值的类型。在有可能丢失信息的情况下进行的转换是通过造型来完成的,但可能造成精度降低或溢出。
强制类型转换的语法格式:“(type)var”,运算符“()”中的type表示将值var想要转换成的目标数据类型。

 例如:double x  = 3.14int nx = (int)x;   //值为3
       char c = 'a';
       int d = c+1;
       System.out.println(d);
       System.out.println((char)d);

当将一种类型强制转换成另一种类型,而又超出了目标类型的表示范围,就会被截断成为一个完全不同的值。

例如: int x = 300;
byte bx = (byte)x; //值为44

不能在布尔类型和任何数值类型之间做强制类型转换。

九、基本类型转化时常见错误和问题

1.操作比较大的数时,要留意是否溢出,尤其是整数操作时。

 int money = 1000000000;  //10亿
 int years = 20;
 int total = money*years;   //返回的是负数
 long total1 = money*years;  //返回的仍然是负数。默认是int,因此结果会转成int值,再转成long。但是已经发生了数据丢失
 long total2 = money*((long)years);   //先将一个因子变成long,整个表达式发生提升。全部用long来计算。
  1. L和l 的问题:
  • 不要命名名字为l的变量
  • long类型使用大写L不要用小写。

int l = 2;
long a = 23451l;
System.out.println(l+1);

十、JDK7新特性:

1、二进制整数
由于我们在开发中也经常使用二进制整数,因此JDK7为我们直接提供了二进制整数的类型。我们只要以:0b开头即可。
int a = 0b0101:
2、下划线分隔符
在实际开发和学习中,如果遇到特别长的数字,读懂它令人头疼!JDK7为我们提供了下划线分隔符,可以按照自己的习惯进行分割,如:
int b = 1_2312_3131;
我们很容易就知道这是1亿2312万3131啦! 非常符合国人的习惯!

十一、变量(variable)

我们通过变量来操纵存储空间中的数据,变量就是指代这个存储空间!空间位置是确定的,但是里面放置什么值不确定!
Java是一种强类型语言,每个变量都必须声明其类型。
Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域。
变量在使用前必须对其声明, 只有在变量声明以后,才能为其分配相应长度的存储单元,声明格式为:

type  varName  [=value] [{,varName[=value]}] ;

注意事项:

  • 每个变量都有类型,类型可以是基本类型,也可以是引用类型。
  • 变量名必须是合法的标识符。
  • 变量声明是一条完整的语句,因此每一个声明都必须以分号结束

变量声明举例:

double salary ; boolean done;
long earthPopulation ; int age ;

可以在一行中声明多个变量:

int i ,j; // both are integers

不提倡这种风格,逐一声明每一个变量可以提高程序可读性。
可以将变量的声明和初始化放在同一行中,例如:

int age = 18; float e = 2.718281828f;

变量可划分为:
局部变量(lacal variable):方法或语句块内部定义的变量。生命周期是从声明位置开始到”}”为止。
在使用前必须先声明和初始化(赋初值)。

public void incorrectLocalV() {
    int i;
    int j = i+5 ; // 编译出错,变量i还未被初始化 
} 

实例变量(成员变量 member variable):方法外部、类的内部定义的变量。从属于对象,生命周期伴随对象始终。
如果不自行初始化,它会自动初始化成该类型的默认初始值(数值型变量初始化成0或0.0,字符型变量的初始化值是16位的0,布尔型默认是false)

静态变量(类变量 static variable):使用static定义。 从属于类,生命周期伴随类始终,从类加载到卸载。
如果不自行初始化,他会自动初始化成该类型的默认初始值(数值型变量初始化成0或0.0,字符型变量的初始化值是16位的0,布尔型默认是false)

public class LocalVariableTest {
    public static void main(String[ ] arg) {
    boolean flag = true;  // 声明boolean型变量并赋值
    char c1, c2;   // 声明char型变量
    c1 = '\u0041';   // 为char型变量赋值
    c2 = 'B';   // 为char型变量赋值
    int x;   // 声明int型变量
    x = 9;  //为int型变量赋值  
    int y = x;  // 声明并初始化int型变量
    float f = 3.15f;   // 声明float型变量并赋值
    double d = 3.1415926;  //声明double型变量并赋值
    }
}

常量(Constant)
常量:初始化(initialize)后不能再改变值!

public class Constants {
    public static void main(String[] args) {
    final double PI = 3.14;
    // PI = 3.15;    //error 
    double r = 4;
    double area = PI * r * r;
    double circle = 2 * PI * r;
    System.out.println("area = " + area);
    System.out.println("circle = " + circle);
    }
}

变量命名规则(规范)
1. 所有变量、方法、类名:见名知意
2. 类成员变量:首字母小写和驼峰原则 : monthSalary
3. 局部变量:首字母小写和驼峰原则
4. 常量:大写字母和下划线:MAX_VALUE(final 只能被初始化一次)
5. 类名:首字母大写和驼峰原则: Man, GoodMan
6. 方法名:首字母小写和驼峰原则: run(), runRun()

 public class TestVariable {
    int t;    //实例变量,成员变量,属性
    public static void main(String[] args){
    int a;
    int b = a+3;
    int x,y,z;
    final int C=34;
    C = 35;
    final int  MAX_SPEED = 120;
    }
   }

十二、运算符

1、Java 语言支持如下运算符:

算术运算符: +,-,,/,%,++,–
赋值运算符 =
关系运算符: >,<,>=,<=,==,!= instanceof
逻辑运算符: &&,||,!
位运算符: &,|,^,~ , >>,<<,>>> (了解!!!)
条件运算符 ?:
扩展赋值运算符:+=,-=,
=,/=

2、二元运算符
整数运算:(类型提升)
如果两个操作数有一个为Long, 则结果也为long
没有long时,结果为int。即使操作数全为shot,byte,结果也是int.
浮点运算:
如果两个操作数有一个为double, 则结果为double.
只有两个操作数都是float, 则结果才为float.
3、取模运算 其操作数可以为浮点数,一般使用整数。如:5.9%3.9=2.000000004
要点:
负数%负数=负数;
负数%正数=负数;
正数%负数=正数;
注:一般都是正整数运算,不用考虑这么细!
4、一元运算符(++, --)

int a = 3;
int b = a++; //执行完后,b=3。先给b赋值,a再自增。
int c = ++a; //执行完后,c=5。a先自增,再给b赋值

注意:java中的乘幂处理:

int a = 3^2; //java中不能这么处理, ^是异或符号。
double b = Math.pow(3, 2);

Math类提供了很多科学和工程计算需要的方法和常数。
(0为false,1为true)
5、布尔逻辑表达符
逻辑与:&&和&,逻辑或:||和|,逻辑非:!。
逻辑与和逻辑或采用短路的方式。从左到右计算,如果确定值则不会再计算下去。
逻辑与只要有一个为false, 则直接返回false.
逻辑或只要有一个为true, 则直接返回true;

boolean c = 1>2&&2>(3/0);

6、位运算符(了解)
 ~ :取反 & : 按位与
 | :按位或 ^ :按位异或
 <<:左移运算符, >>:右移运算符 >>>:无符号移位运算符
右移一位相当于除2取商。
左移一位相当于乘2。

int a = 322;
int b = 3<<2; //相当于:322;
int a = 12/2/2;
int b = 12>>2;

运算符 用法举例 等效的表达式
+= a += b a = a+b
-= a -= b a = a-b
*= a = b a = ab
/= a /= b a = a/b
%= a %= b a= a%b

7、扩展运算符

8、字符串连接符
“+”运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接。

int c = 12;
System.out.println(“c=” + c);

9、三目条件运算符
三目条件运算符,语法格式:

x ? y : z

其中 x 为 boolean 类型表达式,先计算 x 的值,若为true,则整个三目运算的结果为表达式 y 的值,否则整个运算结果为表达式 z 的值。
举例:

int score = 80; int x = -100;
String type = score < 60 ? "不及格" : "及格";
int flag = x > 0 ? 1 : (x == 0 ? 0 : -1);
System.out.println("type= " + type);
System.out.println("flag= "+ flag);

十三、myeclipse常用快捷键

a)命令提示:alt+/ (main, sys)
b)文件选择:
i.Shift+ 上下左右,home,end键
ii.Ctrl+Shift+ 左右 单词选中
c)删除一行:ctrl+d
d)复制一行到下面或上面: ctrl+alt+上下
e)换行:alt+上下
f)编辑文件切换:ctrl+F6,或者ctrl+E,上下
g)视图切换:ctrl+F7
h)查看错误建议:ctrl+1
i)导入类:ctrl+shift+o
j)重命名:alt+shift+r
k)查看类的层次结构:ctrl + t
l)对一个文件中的同一个单词,同步修改,快捷键是:alt+shift+R键,

十四、逻辑结构

1、顺序结构
2、选择结构
if单选择结构
如果条件为true执行一个操作:

意义:if语句对条件表达式进行一次测试,若测试为真,则执行下面的语句,否则跳过该语句

    double i = 6 * Math.random();
    double j = 6 * Math.random();
    double k = 6 * Math.random();
    int count = (int) (i + j + k);
    if(count > 15) {
    System.out.println("今天手气不错");
    }
    if(count >= 10 && count <= 15) {   //错误写法:10<count<15
    System.out.println("今天手气很一般");
    }
    if(count < 10) {
    System.out.println("今天手气不怎么样");
    }
System.out.println("得了" + count + "分");
     Math类的使用:
    int i = (int) (6 * Math.random()); //产生:[0,5]
 

>  if-else双选择结构:如果条件为true执行一个操作,为false执行另一个操作:
>  意义:当条件表达式为真时,执行语句块1,否则,执行语句块2。也就是else部分。


    double r = 4 * Math.random();
    double area = Math.PI * Math.pow(r, 2);
    double circle = 2 * Math.PI * r;
    System.out.println("半径为: " + r);
    System.out.println("面积为: " + area);
    System.out.println("周长为: " + circle);
    if(area >= circle) {
    System.out.println("面积大于等于周长");
    } else {
    System.out.println("周长大于面积");
}

If-elseif-else多选择结构

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

逐条if语句进行判断,条件匹配,进入语句体
否则对if语句继续匹配

public class IfTest3 {
    public static void main(String[] args) {
    int age = (int) (100 * Math.random());
    System.out.print("年龄是" + age + ", 属于");  
    if (age < 15) {
    System.out.println("儿童, 喜欢玩!"); 
    } else if (age < 25) {
    System.out.println("青年, 要学习!"); 
    } else if (age < 45) {
    System.out.println("中年, 要工作!"); 
    } else if (age < 65) {
    System.out.println("中老年, 要补钙!"); 
    } else if (age < 85) {
    System.out.println("老年, 多运动!"); 
    } else {
    System.out.println("老寿星, 古来稀!"); 
    }
  } 
}

3、switch多选择结构
根据表达式值的不同执行许多不同的操作
Switch语句:case标签必须是整数(byte,short,char,int)或者枚举,不能是字符串。
【注:JDK7.0可以使用字符串!】
根据表达式的值,从一系 列代码选出一段执行。格式如下:
switch (表达式) {
case 值1:
语句序列;
[break];
case 值2:
语句序列;
[break];
… … … … …
[default:
默认语句;]
}
switch语句会根据表达式的值从相匹配的case标签处开始执行,一直执行到break语句处或者是switch语句的末尾。与任一case值不匹配,则进入default语句(如果有的话)

/*
 * 测试swtich语句
 */
public class TestSwitch {
	public static void main(String[] args) {
		double d = Math.random();
		int e =1+(int)(d*6);  
		System.out.println(e);
		
		System.out.println("测试多选择结构");
		if(e==6){
			System.out.println("运气非常好!");
		}else if(e==5){
			System.out.println("运气很不错!");
		}else if(e==4){
			System.out.println("运气还行吧");
		}else{   //1,2,3
			System.out.println("运气不好!");
		}
		
		System.out.println("#########################");
		switch(e){   //int,或者自动可以转为int的类型(byte,char,short)。枚举。//JDK7中可以放置字符串。
		case 6:
			System.out.println("运气非常好!");
			break;    //一般在每个case后面都要加break,防止出现case穿透现象。
		case 5:
			System.out.println("运气很不错!");
			break;
		case 4:
			System.out.println("运气还行吧");
			break;
		default:
			System.out.println("运气不好!");
			break;
		}
		
		System.out.println("***************************下面例子反过来利用了case穿透现象!");
		char  c = 'a';
		int rand =(int) (26*Math.random());
		char c2 = (char)(c+rand);
		System.out.print(c2 + ": ");
		switch (c2) {
		case 'a':
		case 'e':
		case 'i':
		case 'o':
		case 'u':
			System.out.println("元音");  
			break;
		case 'y':
		case 'w':
			System.out.println("半元音");   
			break;
		default:
			System.out.println("辅音");
		}		
	}
}

十五、循环结构

1、While循环
while循环的基本格式和流程图:

while (布尔表达式) {
循环体;
}

while循环详解:
在循环刚开始时,会计算一次“布尔表达式”的值,若条件为真,执行循环体。而对于后来每一次额外的循环,都会在开始前重新计算一次。
语句中应有使循环趋向于结束的语句,否则会出现无限循环–––"死"循环。

while循环代码示例

public class WhileTest {
    public static void main(String[] args) {
    int  i = 0;
    int  sum = 0;
    while (i <= 100) {   // 1+2+3+…+100=?
    sum += i;   //sum = sum+i;
    i++;
    }
    System.out.println("Sum= " + sum);
    }
}

2、Do-while循环
(用的很少,了解!!)
do-while循环的基本格式:

do {
循环体;
} while(布尔表达式) ;

do-while循环说明和流程图:

  1. 先执行循环体
  2. 后判断布尔表达式
  3. 循环体至少执行一次
public class Test {
    public static void main(String[] args) {
    int i = 0;
    int sum = 0;
do {
    sum += i; // sum = sum + i
    i++;
} while (i <= 100);
    System.out.println("Sum= " + sum);
}
}

While和do-While的区别:
while先判断后执行。dowhile是先执行后判断!
Dowhile总是保证循环体会被至少执行一次!这是他们的主要差别。

int a = 0;
while(a<0){
    System.out.println(a);
    a++;
}
    System.out.println("-----");
    a=0;
do{
    System.out.println(a);
    a++;
} while (a<0);

3、For循环
for循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构
语法形式

for (初始表达式;布尔表达式;步进) {
循环体;
}

for循环在第一次反复之前要进行初始化。随后,它会进行条件测试,而且在每一次反复的时候,进行某种形式的“步进”。
1.初始化部分设置循环变量的初值
2.条件判断部分为任意布尔表达式
3.迭代因子控制循环变量的增减

【注意】for循环在执行条件测试后,先执行程序部分,再执行步进。

/*
 * 测试For循环语句
 * 
 */
public class TestFor {
	public static void main(String[] args) {
		int a = 1;    //初始化
		while(a<=100){  //条件判断
			System.out.println(a);    //循环体
			a+=2;  //迭代
		}
		System.out.println("while循环结束!");
		
		    
		for(int i = 1;i<=100;i++){  //初始化//条件判断 //迭代
			System.out.println(i);    //循环体
		}
		System.out.println("while循环结束!");
	}
}

循环综合练习01:

/*
 * while和for循环
 */
public class TestWhileFor {
	public static void main(String[] args) {
//计算0到100之间的奇数和偶数的和
		int oddSum = 0;  //用来保存奇数的和
		int evenSum = 0;  //用来存放偶数的和
		for(int i=0;i<=100;i++){
			if(i%2!=0){
				oddSum += i;
			}else{
				evenSum += i;
			}
			
		}
		System.out.println("奇数的和:"+oddSum); 
		System.out.println("偶数的和:"+evenSum); 
		
		System.out.println("#########################"); 
	//用while和for循环输出1-1000之间能被5整除的数,并且每行输出3个
		for(int j = 1;j<=1000;j++){
			if(j%5==0){
				System.out.print(j+"\t");
			}
			if(j%(5*3)==0){
				System.out.println();
			}
		}
		
	}
}

打印九九乘法表

/*
 * 测试控制语句练习*  
 */
public class TestWhileFor02 {
    public static void main(String[] args) {
    for (int m = 1; m <= 9; m++) {
    for (int i = 1; i <= m; i++) {
    System.out.print(i + "*" + m + "=" + (i * m) + "\t");
}
    System.out.println();
}
 }
}

十六、break语句

在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句。(break语句也在switch语句中使用)

//循环输出随机数,直到输出88为止!
public class BreakTest {
public static void main(String[] args) {
int total = 0;
System.out.println(“Begin”);
while (true) {
total++;
int i = (int) Math.round(100 * Math.random());
if (i == 88)
break;
}
System.out.println(“Game over, used " + total + " times.”);
}
}

十七、continue语句
continue 语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。

//把100~150之间不能被3整除的数输出:
public class ContinueTest {
public static void main(String[] args) {
for (int i = 100; i < 150; i++) {
if (i % 3 == 0)
continue;
System.out.println(i);
}
}
}
十八、带标签的break和continue语句
(了解即可)
1.goto关键字很早就在程序设计语言中出现。尽管goto仍是Java的一个保留字,但并未在语言中得到正式使用;Java没有goto。然而,在break和continue这两个关键字的身上,我们仍然能看出一些goto的影子—带标签的break和continue。
2.“标签”是指后面跟一个冒号的标识符,例如:label:
3.对Java来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于break和continue关键字通常只中断当前循环,但若随同标签使用,它们就会中断到存在标签的地方。
4.带标签的break和continue的例子:
public class PrimeNumber { //打印101-150之间所有的质数
public static void main(String args[]) {
int count = 0;
outer: for (int i = 101; i < 150; i ++) {
for (int j = 2; j < i / 2; j++) {
if (i % j == 0)
continue outer;
}
System.out.print(i+ " ");

			}
}

}
十九、方法
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。 我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,
方法的要点
Java的方法类似于其它语言的函数,是一段用来完成特定功能的代码片段,声明格式:
[修饰符1 修饰符2 …] 返回值类型 方法名(形式参数列表){
Java语句;… … …
}
形式参数:在方法被调用时用于接收外界输入的数据。
实参:调用方法时实际传给方法的数据。
返回值:方法在执行完毕后返还给调用它的环境的数据。
返回值类型:事先约定的返回值的数据类型,如无返回值,必须给出返回值类型void。
Java语言中使用下述形式调用方法:对象名.方法名(实参列表)
实参的数目、数据类型和次序必须和所调用方法声明的形参列表匹配,
return 语句终止方法的运行并指定要返回的数据。
Java中进行方法调用中传递参数时,遵循值传递的原则:
基本类型传递的是该数据值本身。引用类型传递的是对对象的引用,而不是对象本身。
JAVA中只有值传递!
/*

  • 测试方法

*/
public class TestMethod {

//设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。   我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,
public static void test01(int a) {
	int oddSum = 0; // 用来保存奇数的和
	int evenSum = 0; // 用来存放偶数的和
	for (int i = 0; i <= a; i++) {
		if (i % 2 != 0) {
			oddSum += i;
		} else {
			evenSum += i;
		}

	}
	System.out.println("奇数的和:" + oddSum);
	System.out.println("偶数的和:" + evenSum);
}

public static void test02(int a,int b,int c){
	for (int j = 1; j <= a; j++) {
		if (j % b == 0) {
			System.out.print(j + "\t");
		}
		if (j % (b * c) == 0) {
			System.out.println();
		}
	}
}

public static int add(int a,int b){
	int sum = a+b;
	if(a==3){
		return 0;    //return 两个作用:结束方法的运行、返回值。
	}
	System.out.println("输出");
	return sum;
}

public static void main(String[] args) {
	test01(1000);
	test02(100,6,3);  //1-100之间,可以被6整除,每行输出3个。
	System.out.println("###########");
	int s = add(3,5);
	System.out.println(s);

}

}

二十、递归算法
A方法调用B方法,我们很容易理解!递归就是:A方法调用A方法!就是自己调用自己,因此我们在设计递归算法时,一定要指明什么时候自己不调用自己。否则,就是个死循环!
递归算法要点
递归是一种常见的解决问题的方法,即把问题逐渐简单化。递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己。
利用递归可以用简单的程序来解决一些复杂的问题。比如:大多数排序使用的就是递归算法。
递归结构包括两个部分:
1. 定义递归头。解答:什么时候不调用自身方法。如果没有头,将陷入死循环。
2. 递归体。解答:什么时候需要调用自身方法。
/*

  • 测试递归算法
    /
    public class TestRecursion {
    static int a = 0;
    public static void test01(){
    a++;
    System.out.println(“test01:”+a);
    if(a<=10){ //递归头
    test01();
    }else{ //递归体
    System.out.println(“over”);
    }
    }
    public static void test02(){
    System.out.println(“TestRecursion.test02()”);
    }
    public static long factorial(int n){
    if(n==1){
    return 1;
    }else{
    return n
    factorial(n-1);
    }
    }
    public static void main(String[] args) {
    test01();
    System.out.println(factorial(10));
    }
    }
    代码中阶乘代码的调用执行过程,参见下图,可以放大看:

二十一、API
API:Application Programming Interface,应用程序编程接口。
API文档是什么?
JDK为我们提供了很多类,如何使用这些类。我们可以通过查看API文档来获取信息。API文档就相当于我们学英语用的词典一样!

二十二、package包的概念
为什么需要package?
为了便于管理类:合适的类位于合适的包!
为了解决类之间的重名问题。
package怎么用?
通常是类的第一句非注释性语句。
包名:域名倒着写即可,再加上模块名,并与内部管理类。
其实内部实现就是靠目录结构来做到的。
com.sun.test
com.oracle.test
com.bjsxt.gao.test
com.bjsxt.gao.view
com.bjsxt.gao.view.model
注意事项:
写项目时都要加包,不要使用默认包。
com.gao和com.gao.car,这两个包没有包含关系,是两个完全独立的包。只是逻辑上看起来后者是前者的一部分。
JDK中的主要包
java.lang-包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。
java.awt-包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
java.net-包含执行与网络相关的操作的类。
java.io-包含能提供多种输入/输出功能的类。
java.util-包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。
如何生成自己项目的API文档?
• 特殊的注释:
– 文档注释:/**
– 使用JAVADOC生成API文档
– 解决问题:代码和文档的分离
• 常用的java注释标签:
– @Author 作者
– @version 版本
– @param 参数
– @return 返回值的含义
– @throws 抛出异常描述
– @deprecated 废弃。建议用户不在使用该方法
创建api文档的步骤

  1. 右键单击项目,选中export

二十三、import
为什么需要import?
如果不适用import,我们如果用到其他包的类时,只能这么写:java.util.Date,代码量太大,不利于编写和维护。通过import可以导入其他包下面的类,从而可以在本类中直接通过类名来调用。
import怎么使用?
import java.util.Date;
import java.util.*; //导入该包下所有的类。会降低编译速度,但不会降低运行速度。
注意要点:
1. java会默认导入java.lang包下所有的类,因此这些类我们可以直接使用。
2. 如果导入两个同名的类,只能用包名+类名来显示调用相关类:
java.util.Date date = new java.util.Date();

静态导入
static import静态导入(JDK 1.5后增加, 了解!!!!):
静态导入的作用:用于导入指定类的静态属性
如何使用:
import static java.lang.Math.*;//导入Math类的所有静态属性
import static java.lang.Math.PI;//导入Math类的PI属性
然后,我们可以在程序中直接使用:System.out.println(PI);
(通过Scanner类练习import以及了解如何获取键盘的输入信息):
import java.util.Scanner;
/**

  • 测试import导入Scanner类,以及Scanner类的使用,如何接收键盘的输入。

*/
public class TestScanner {
public static void test01(){
Scanner s = new Scanner(System.in);
String str = s.next(); //程序运行到next会阻塞,等待键盘的输入!
System.out.println(“刚才键盘输入:”+str);
}
public static void test02(){
Scanner s = new Scanner(System.in);
System.out.println(“请输入一个加数:”);
int a = s.nextInt();
System.out.println(“请输入被加数:”);
int b = s.nextInt();
int sum =a+b;
System.out.println(“计算结果,和为:”+sum);
}
public static void main(String[] args) {
test02();
}
}

二十四、面向对象和面向过程的本质区别
语言的进化发展跟生物的进化发展其实是一回事,都是”物以类聚”。相近的感光细胞聚到一起变成了我们的眼睛,相近的嗅觉细胞聚到一起变成了我们的鼻子。
语句多了,我们将完成同样功能的相近的语句,聚到了一块儿,便于我们使用。于是,方法出现了!
变量多了,我们将功能相近的变量组在一起,聚到一起归类,便于我们调用。于是,结构体出现了!
再后来,方法多了,变量多了!结构体不够用了!我们就将功能相近的变量和方法聚到了一起,于是类和对象出现了!
面向过程: 1/踩离合 2/挂挡 3/踩油门,放离合 4/开了
面向对象: 驾驶员 汽车 驾驶员开汽车
面向对象编程(oop)的本质
面向对象编程的本质就是:以类的方式组织代码,以对象的方式组织(封装)数据。
抽象是什么?就是将多个物体共同点归纳出来,就是抽出像的部分!
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。
从代码运行角度考虑是先有类后有对象。类是对象的模板。
如下图深刻的揭示了类和对象的关系:

package cn.bjsxt.oop;

public class Student {
//静态的数据
String name;
int id; //学号
int age;
String gender;
int weight;
//动态的行为
public void study(){
System.out.println(name+“在學習”);
}
public void sayHello(String sname){
System.out.println(name+“向”+sname+“說:你好!”);
}
}
public class Test1 {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = “蒋蒋”;
s1.study();
s1.sayHello(“景”);
Student s2 = new Student();
s2.age = 18;
s2.name=“蒋越”;
}
}
二十五、JAVA程序运行的内存分析
栈:
1. 每个线程私有,不能实现线程间的共享!
2. 局部变量放置于栈中。
3. 栈是由系统自动分配,速度快!栈是一个连续的内存空间!
堆:
1. 放置new出来的对象!
2. 堆是一个不连续的内存空间,分配灵活,速度慢!
方法区(也是堆):
1. 被所有线程共享!
2. 用来存放程序中永远是不变或唯一的内容。(类的信息、静态变量、常量池(字符串常量))
【注】本次内存分析,我们的主要目的是让大家了解基本的内存概念。类加载器、Class对象这些更加详细的内容,我们将在后面专门讲反射的课程里面讲。
引用类型的概念
1. java中,除了基本数据类型之外的其他类型称之为引用类型。
2. java中的对象是通过引用来操作的。(引用:reference)
. 说白了,引用指的就是对象的地址!
属性(field,或者叫成员变量)
1. 属性用于定义该类或该类对象包含的数据或者说静态属性。
2. 属性作用范围是整个类体。
3. 属性的默认初始化:
在定义成员变量时可以对其初始化,如果不对其初始化,Java使用默认的值对其初始化。(数值:0,0.0 char:\u0000, boolean:false, 所有引用类型:null)
4. 属性定义格式:
[修饰符] 属性类型 属性名 = [默认值]
类的方法
方法是类和对象动态行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成;面向对象中,整个程序的基本单位是类,方法是从属于类或对象的。
方法定义格式:
[修饰符] 方法返回值类型 方法名(形参列表) {
// n条语句
}
java对象的创建和使用
³ 必须使用 new 关键字创建对象。
² Person person= new Person ();
³ 使用对象(引用) . 成员变量来引用对象的成员变量。
² person.age
³ 使用对象(引用) . 方法(参数列表)来调用对象的方法。
² person. setAge(23)
代码和内存分析结果
package cn.bjsxt.oop;
public class Student {
//静态的数据
String name;
int id; //学号
int age;
String gender;
int weight;

//动态的行为
public void study(){
System.out.println(name+“在學習”);
}

public void sayHello(String sname){
System.out.println(name+“向”+sname+“說:你好!”);
}
}
package cn.bjsxt.oop;
public class Test1 {
public static void main(String[] args) {
//通过类加载器Class Loader加载Student类。 加载后,在方法区中就有了Student类的信息!
Student s1 = new Student();
s1.name = “蒋蒋”;
s1.study();
s1.sayHello(“景”);
Student s2 = new Student();
s2.age = 18;
s2.name=“蒋越”;
}
}
上面Test1类执行的内存分析图如下(更加详细的分析流程,参见31集视频):

package cn.bjsxt.oop;
public class Computer {
String brand;
int cpuSpeed;
}
package cn.bjsxt.oop;
public class Student {
//静态的数据
String name;
int id; //学号
int age;
String gender;
int weight;
Computer computer;
//动态的行为
public void study(){
System.out.println(name+“在學習”);
}
public void sayHello(String sname){
System.out.println(name+“向”+sname+“說:你好!”);
}
}
package cn.bjsxt.oop;
public class Test2 {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = “蒋蒋”;
s1.age = 18;
Computer c = new Computer();
c.brand = “联想”;
c.cpuSpeed = 100;
s1.computer = c;
c.brand = “戴尔”;
System.out.println(s1.computer.brand);
}
}

二十六、垃圾回收机制(Garbage Collection)
JAVA的内存管理实际上指的就是对象的管理,其中包括对象空间的分配和释放。
对象空间的分配
使用new关键字创建对象即可
对象空间的释放
将对象赋值null,即该对象没有引用指向时。垃圾回收器将负责回收所有”不可达”对象的内存空间。

要点
1.程序员无权调用垃圾回收器。
2.程序员可以通过System.gc()。通知GC运行,但是JAVA规范并不能保证立刻运行。
3.finalize方法,是JAVA提供给程序员用来释放对象或资源的方法,但是尽量少用。
二十七、构造器(constructor,或者叫构造方法)
构造器作用:
构造器用于构造该类的实例。
构造器调用:
Java通过new关键字来调用构造器,从而返回该类的实例。
构造器定义的格式:
[修饰符] 类名(形参列表){
//n条语句
}
构造器使用要点
1. 通过new关键字调用!!
2. 构造器虽然有返回值(返回该类的对象),但是不能定义返回类型 (返回值的类型肯定是本类),不能在构造器里调用return。
3. 如果我们没有定义构造器,则系统会自动定义一个无参的构造函数。如果已定义则编译器不会添加!
4. 构造器的方法名必须和类名一致!
5.作用:构造该类的对象,经常也用来初始化对象的属性。

构造器简单代码测试
package cn.bjsxt.oop;
public class Car {
int speed;
public Car(){
System.out.println(“构造一个车”);
}
}
package cn.bjsxt.oop;
/*

  • 测试构造方法

*/
public class TestConstructor {
public static void main(String[] args) {
Car c = new Car();
}
}
³ 定义一个“点”(Point)类用来表示三维空间中的点(有三个坐标)。要求如下:
² 可以生成具有特定坐标的点对象。
² 提供可以设置三个坐标的方法。
² 提供可以计算该“点”距另外点距离的方法。

package cn.bjsxt.oop;

public class Point {
double x,y,z;
//通过构造方法初始化对象的属性
public Point(double x,double y, double z){
x =x;
y = y;
z = z;
}
public void setX(double _x){
x = _x;
}
public void setY(double _y){
y = _y;
}
public void setZ(double _z){
z = _z;
}
public double distance(Point p){
double result = Math.sqrt((x-p.x)(x-p.x)+(y-p.y)(y-p.y)+(z-p.z)(z-p.z));
return result;
}
public static void main(String[] args) {
Point p = new Point(3,4,8);
Point p2 = new Point(200,40,80);
p.setX(10);
System.out.println(p.x);
System.out.println(p.distance(p2));
}
}
二十八、方法的重载(overload),构造方法的重载
方法的重载是指一个类中可以定义有相同的名字,但参数不同的多个方法。 调用时,会根据不同的参数表选择对应的方法。
方法调用时,匹配离得最近的类型
不同的含义:类型,个数,顺序不同
只有返回值不同不构成方法的重载
只有形参的名称不同,不构成方法的重载
与普通方法一样,构造方法也可以重载
package cn.bjsxt.oop;
/

  • 测试重载
    */
    public class TestOverload {
    public int add(int a, int b){
    return a+b;
    }
    public static void main(String[] args) {
    MyMath m = new MyMath();
    int result = m.add(4.2,8);
    System.out.println(result);
    }
    }

class MyMath {
int a;
int b;
public MyMath(){
}

public MyMath(int a){
this.a = a;
}
public MyMath(int b, int a){
this.b = b;
this.a = a;
}
public int add(int b, double a){
return (int)(a+b);
}
public int add(double a, int b){
return (int)(a+b);
}
public int add(int a, int b){
return a+b;
}
public int add(int a, int b,int c){
return a+b+c;
}
}
二十九、static关键字
在类中,用static声明的成员变量为静态成员变量.或者叫:类变量、类属性
1.它为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化,
2.对于该类的所有对象来说,static成员变量只有一份。被该类的所有对象共享!!
3.可以使用”对象.类属性”来调用。不过,一般都是用“类名.类属性”。
4.用static声明的方法为静态方法
5.不需要对象,就可以调用(类名.方法名)
在调用该方法时,不会将对象的引用传递给它,所以在static方法中不可访问非static的成员。
可以通过对象引用或类名(不需要实例化)访问静态成员。
代码
package cn.bjsxt.oop.testStatic;
public class Student {
String name;
int id;
static int ss;
public static void printSS(){
System.out.println(ss);
}

public void study(){
printSS();
System.out.println(name+“在學習”);
}

public void sayHello(String sname){
System.out.println(name+“向”+sname+“說:你好!”);
}
}
package cn.bjsxt.oop.testStatic;
public class Test {
public static void main(String[] args) {
Student.ss = 323;
Student.printSS();
Student s1 = new Student();
}
}
static课堂代码的内存分析图如下(想查看具体怎么画的流程,请看相应的视频课程):

三十、this关键字
普通方法中,this总是指向调用该方法的对象。
构造方法中,this总是指向正要初始化的对象。

this最常的用法:
1. 让类中的一个方法,访问该类的另一个方法或属性。
2. 使用this关键字调用重载构造方法。避免相同的初始化代码,只能在构造方法中用,并且必须位于构造方法的第一句。
this使用时的注意事项:
·this不能用于static方法!(this指向当前对象,static方法跟对象没有一毛钱的关系)
代码
package cn.bjsxt.oop.testThis;
public class Student {
String name;
int id;
public Student(String name,int id){
this(name); //通过this调用其他构造方法,必须位于第一句! Constructor call must be the first statement in a constructor
this.name = name;
this.id = id;
}

public Student(String name){
this.name = name;
}
public Student(){
System.out.println(“构造一个对象”);
}

public void setName(String name){
this.name = name;
}

public void study(){
this.name= “张三”;
System.out.println(name+“在學習”);
}
public void sayHello(String sname){
System.out.println(name+“向”+sname+“說:你好!”);
}
}
三十一、继承(方便建模和提高代码复用率)
为什么需要继承?继承的作用?
第一好处:继承的本质在于抽象。类是对对象的抽象,继承是对某一批类的抽象。
第二好处:为了提高代码的复用性。
extands的意思是“扩展”。子类是父类的扩展。
【注】JAVA中类只有单继承没有多继承! 接口可以多继承!
继承例子:比如
动物:
哺乳动物、爬行动物
哺乳动物:
灵长目、鲸目等。
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
如何实现继承?
使用extends关键字即可。
小结1:子类继承父类,可以得到父类的全部属性和全部方法(除了父类的构造方法)
小结2:JAVA中类只有单继承没有像C++那样的多继承
小结3:java中的多继承可以通过接口实现
小结4:如果定义一个类时,没有调用extends,则父类为:java.lang.Object;
课堂代码
package cn.bjsxt.oop.inherit;
/*

  • 测试继承

*/
public class Animal {
String eye;

public void run(){

System.out.println(“跑跑!”);
}
public void eat(){
System.out.println(“吃吃!”);
}
public void sleep(){
System.out.println(“zzzzz”);
}

public Animal(){
System.out.println(“创建一个动物!”);
}

}

class Mammal extends Animal {

public void taisheng(){
System.out.println("我是胎生");

}

}

class Bird extends Animal {

public void eggSheng(){
System.out.println("卵生");

}

}
package cn.bjsxt.oop.inherit;

public class Test {
public static void main(String[] args) {
Bird b = new Bird();
b.run();
}
}
三十二、方法的重写(override)
³ 在子类中可以根据需要对从基类中继承来的方法进行重写。
³ 重写方法必须和被重写方法具有相同方法名称、参数列表和返回类型。
³ 重写方法不能使用比被重写方法更严格的访问权限。(由于多态)
方法重写和方法重载的区别
很多人问重写和重载什么区别? 他们两个完全是两回事。除了名字都带一个“重”字之外。
方法重载指的是:同一个类中,一个方法名对应了多个方法(形参列表不同)
方法的重写指的是:子类重写了父类的方法!
课堂代码
package com.jiang.override;
/**

  • 测试重写
  • @author 蒋蒋

*/
public class Animal {
String eye;

public void run(){
	System.out.println("跑跑!");
}

public void eat(){
	System.out.println("进食!");
}

}

class Mammal extends Animal{

public void taisheng(){
	this.run();
	System.out.println("胎生");
}

}

class Bird extends Animal{

public void eggsheng(){
	System.out.println("卵生");
}

/*测试重写,重写了父类的run()方法*/
public void run() {
	this.eat();
	super.run();
	System.out.println("飞啊飞");
}

}
package com.jiang.override;

public class Test {
public static void main(String[] args) {
Bird bird = new Bird();
bird.run();
}
}
三十三、Object类
³ Object类是所有Java类的根基类(java.lang.Object)
³ 如果在类的声明中未使用extends关键字指明其基类,则默认基类为Object类
public class Person {

}
³ 等价于:
public class Person extends Object {

}
toString方法
³ Object类中定义有public String toString()方法,其返回值是 String 类型,描述当前对象的有关信息。
³ 在进行String与其它类型数据的连接操作时(如:System.out.println(“info”+person)),将自动调用该对象类的 toString()方法
³ Object类中toString方法的源代码是:
public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
}

默认是打印:类名+哈希码(根据对象地址计算得出 )!
³ 可以根据需要在用户自定义类型中重写toString()方法。比如String类就重写了toString方法,源代码如下:
public String toString() {
return this;
}

equals方法
³ Object类中定义有:
² public boolean equals(Object obj)方法
± 提供定义对象是否“相等”的逻辑。
² Object 的 equals 方法 定义为:x.equals ( y ) 当 x 和 y是同一个对象的应用时返回 true 否则返回 false
查看Object类中的equals方法源代码可以看出:
public boolean equals(Object obj) {
return (this == obj);
}

² J2SDK提供的一些类,如String,Date等,重写了Object的equals方法,调用这些类的equals方法, x.equals (y) ,当x和y所引用的对象是同一类对象且属性内容相等时(并不一定是相同对象),返回 true 否则返回 false。
String类的equals方法显然是比较数组中的char值是否相等,源代码摘录如下:

² 可以根据需要在用户自定义类型中重写equals方法。
课堂代码
package cn.bjsxt.oop.testObject;
public class Mobile {

public String toString(){
return “我是一部移动电话”;
}
}
package cn.bjsxt.oop.testObject;

public class TestObject {
public static void main(String[] args) {
Object obj = new Object();
Object obj2 = new Object();
System.out.println(obj.toString());
System.out.println(obj2.toString());
System.out.println(obj==obj2);
System.out.println(obj.equals(obj2));

String str;

Mobile m = new Mobile();
System.out.println(m.toString()); 

}
}
三十四、super
super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。
this是指向当前对象的引用
除了Object类之外其他所有类的构造方法第一句总是super(…)
任何类的构造方法中,若是构造方法的第一行代码没有显式的调用super(…);那么Java默认都会调用super(); 所以你这里的super(); 加不加都无所谓。

课堂代码
package cn.bjsxt.oop.inherit;
/*

  • 测试继承

*/
public class Animal /extends Object/ {
String eye;

public void run(){
System.out.println("跑跑!");

}
public void eat(){
System.out.println(“吃吃!”);
}
public void sleep(){
System.out.println(“zzzzz”);
}

public Animal(){
super();
System.out.println(“创建一个动物!”);
}

}

class Mammal extends Animal {

public void taisheng(){
System.out.println("我是胎生");

}

}

class Bird extends Animal {
public void run(){
super.run();
System.out.println(“我是一个小小小小鸟,飞呀飞不高”);
}

public void eggSheng(){
System.out.println(“卵生”);
}

public Bird(){
super();
System.out.println(“建一个鸟对象”);
}
}
package cn.bjsxt.oop.inherit;
public class Test {

public static void main(String[] args) {
Bird b = new Bird();
b.run();
}
}

三十五、继承和组合
共同点:都可以实现代码的复用
不同点:
is-a关系:使用继承
has-a关系:使用组合
课堂代码
使用组合实现前几节课用继承实现的关系:Animal、Mammal、Bird:

package cn.bjsxt.oop.inherit;

/**

  • 测试组合
  • @author 尚学堂高淇www.bjsxt.cn

*/
public class Animal2 {
String eye;
public void run(){
System.out.println(“跑跑!”);
}
public void eat(){
System.out.println(“吃吃!”);
}
public void sleep(){
System.out.println(“zzzzz”);
}
public Animal2(){
super();
System.out.println(“创建一个动物!”);
}
public static void main(String[] args) {
Bird2 b = new Bird2();
b.run();
b.animal2.eat();
}
}

class Mammal2 {
//Animal2 animal2 ,空指针error
Animal2 animal2=new Animal2(); //组合
public void taisheng(){
System.out.println(“我是胎生”);
}
}

class Bird2 {
Animal2 animal2=new Animal2();
public void run(){
animal2.run();
System.out.println(“我是一个小小小小鸟,飞呀飞不高”);
}
public void eggSheng(){
System.out.println(“卵生”);
}
public Bird2(){
super();
System.out.println(“建一个鸟对象”);
}
}
三十六、final的用法
修饰变量:
被final修饰的变量不可改变。即初始化一次后,就不能再次被赋值!即,变为常量!
修饰方法:
被final修饰的方法不可以被子类重写!
修饰类:
被final修饰的类不能被继承!
课堂代码
package cn.bjsxt.oop.testFinal;

public class TestFinal {
public static void main(String[] args) {
final int MAX_VALUE= 200; //常量。
double d = Math.PI;
}
}
package cn.bjsxt.oop.testFinal;
public /final/ class Animal { //final修饰类则说明,这个类不能被继承!
public /final/ void run(){ //final加到方法前面,意味着该方法不能被子类重写!
System.out.println(“跑跑!”);
}
}

class Bird extends Animal {
public void run(){
super.run();
System.out.println(“我是一个小小小小鸟,飞呀飞不高”);
}
}
三十七、面向对象三大特征:
继承、封装、多态
封装(encapsulation)
为什么需要封装?封装的作用和含义?
我要看电视,只需要按一下开关和换台就可以了。有必要了解电视机内部的结构吗?有必要碰碰显像管吗?
我要开车,….

制造厂家为了方便我们使用电视,方便我们开车,把复杂的内部细节全部封装起来,只给我们暴露简单的接口,比如:电源开关、比如:油门。具体怎么内部实现的,我们不需要操心。

需要让用户知道的暴露出来,不需要让用户了解的全部隐藏起来。这就是封装。

白话:该露的露,该藏的藏
专业:我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。

编程中封装的具体意义
便于调用者调用。
良好的封装,便于修改内部代码,提高可维护性。
良好的封装,可进行数据完整性检测,保证数据的有效性。
使用访问控制符,实现封装
à范围越来越大
同一个类 同一个包中 子类 所有类
Private(只能在自己类中看到) *
default(即不加修饰符的时候) * *
protected * * *
public * * * *

封装的使用细节

  1. 属性一般使用private.(除非本属性确定会让子类继承并且使用)
    提供相应的get/set方法来访问相关属性. 这些方法通常是public,从而提供对属性的读取操作。 (注意:boolean变量的get方法是用:is开头!)
  2. 一些只用于本类的辅助性方法可以用private,希望其他类调用的方法用public。

课堂代码
package cn.bjsxt.oop.encapsulation01;

public class Man {
private String name;
private String id;
private boolean man;
public static int cc;
public static final int MAX_SPEED = 120;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public boolean isMan() {
return man;
}
public void setMan(boolean man) {
this.man = man;
}
}
package cn.bjsxt.oop.encapsulation01;
public class Test01 {
private String str;
protected void print(){
String s = str;
System.out.println(“Test01.print()”);
}
}

class Test001 extends Test01 {
public void pp(){
super.print();
}
public static void main(String[] args) {
Test01 t = new Test01();
t.print();
}
}

package cn.bjsxt.oop.encapsulation01;

public class Test02 {
public static void main(String[] args) {
Test01 t = new Test01();
t.print();
}
}
package cn.bjsxt.oop.encapsulation02;
import cn.bjsxt.oop.encapsulation01.Test01;
public class Test0001 extends Test01 {
public void ttt(){
super.print();
print();
}
public static void main(String[] args) {
Test0001 t = new Test0001();
t.print();
}
}

package cn.bjsxt.oop.encapsulation02;
import cn.bjsxt.oop.encapsulation01.Test01;

public class Test03 {
public static void main(String[] args) {
Test01 t = new Test01();
// t.print();
}
}

三十八、多态(polymorphism)
多态性是OOP中的一个重要特性,主要是用来实现动态联编的,换句话说,就是程序的最终状态只有在执行过程中才被决定而非在编译期间就决定了。这对于大型系统来说能提高系统的灵活性和扩展性。
多态可以让我们不用关心某个对象到底是什么具体类型,就可以使用该对象的某些方法,从而实现更加灵活的编程,提高系统的可扩展性。
为什么需要多态?
多态性是指允许不同类的对象对同一消息作出不同的响应。
java中如何实现多态?使用多态的好处?
引用变量的两种类型:
编译时类型(模糊一点,一般是一个父类)
由声明时的类型决定。
运行时类型(运行时,具体是哪个子类就是哪个子类)
由实际对应的对象类型决定。通过动态绑定调用该对象的方法。动态绑定会使我们的程序编写更加灵活,但是会减慢程序运行速度。这也是JAVA比C++/C慢的主要原因之一。
多态:如果编译时类型和运行时类型不一致,就会造成多态。
方法绑定(method binding)
执行调用方法时,系统根据相关信息,能够执行内存地址中代表该方法的代码。分为静态绑定和动态绑定。
静态绑定:
在编译期完成,可以提高代码执行速度。
动态绑定:
通过对象调用的方法,采用动态绑定机制。 这虽然让我们编程灵活,但是降低了代码的执行速度。这也是JAVA比C/C++速度慢的主要因素之一。 JAVA中除了final类、final方、static方法,所有方法都是JVM在运行期才进行动态绑定的。
应该针对成员变量、static方法等做一个专门的动态绑定的课程:
http://blog.sina.com.cn/s/blog_600046120100wdza.html
http://blog.csdn.net/aaaaaaaa0705/article/details/6263213
http://www.doc88.com/p-389664843916.html
多态的使用要点
1. 多态是方法的多态,属性没有多态性。
2. 编写程序时,如果想调用运行时类型的方法,只能进行类型转换。不然通不过编译器的检查。但是如果两个没有关联的类进行强制转换,会报:ClassCastException。 比如:本来是狗,我把它转成猫。就会报这个异常。
3. 多态的存在要有3个必要条件:要有继承,要有方法重写,父类引用指向子类对象

对象的转型(casting)
为什么需要强制转换类型?
引用变量只能调用它编译类型的方法,不能调用它运行类型的方法。这时,我们就需要进行类型的强制转换!
³ 一个父类的引用类型变量可以“指向”其子类的对象。
³ 一个父类的引用不可以访问其子类对象新增加的成员(属性和方法)。
³ 可以使用 引用 变量 instanceof 类名 来判断该引用型变量所“指向”的对象是否属于该类或该类的子类。
³ 子类的对象可以当作基类的对象来使用称作向上转型(upcasting),反之称为向下转型(downcasting)

课堂代码
package cn.bjsxt.oop.polymorphism;
public class Animal {
String str;
public void voice(){
System.out.println(“普通动物叫声!”);
}
}

class Cat extends Animal {
public void voice(){
System.out.println(“喵喵喵”);
}
public void catchMouse(){
System.out.println(“抓老鼠”);
}
}

class Dog extends Animal {
public void voice(){
System.out.println(“汪汪汪”);
}
public void seeDoor(){
System.out.println(“看门!”);
}
}

class Tiger extends Animal {
public void voice(){
System.out.println(“哇哇哇”);
}

}

class Pig extends Animal {
public void voice(){
System.out.println(“哼哼哼”);
}
}

package cn.bjsxt.oop.polymorphism;

public class Test {
public static void testAnimalVoice(Animal c){ //Animal c,即为编译时类型
c.voice();
if(c instanceof Cat){
((Cat) c).catchMouse();
}
}
/*
public static void testAnimalVoice(Dog c){
c.voice();
}
public static void testAnimalVoice(Pig c){
c.voice();
}*/
//javac Test.java
// java Test
public static void main(String[] args) {
Animal a = new Cat();//父类引用指向子类对象
Cat a2 = (Cat)a;
testAnimalVoice(a);//此处a对的类型为运行时类型

// a2.catchMouse();

// Animal b = new Dog();
// Animal c = new Tiger();
// testAnimalVoice(b);
// testAnimalVoice©;
}
}

注:this永远指向当前对象!
课堂代码
package cn.bjsxt.oop.polymorphism.myServlet;

public class HttpServlet {
public void service(){
System.out.println(“HttpServlet.service()”);
this.doGet();
}

public void doGet(){
System.out.println(“HttpServlet.doGet()”);
}

}
package cn.bjsxt.oop.polymorphism.myServlet;
public class MyServlet extends HttpServlet {

public void doGet(){
System.out.println(“MyServlet.doGet()”);
}

}

package cn.bjsxt.oop.polymorphism.myServlet;
public class Test {

public static void main(String[] args) {
HttpServlet s = new MyServlet();
s.service();
}
}

三十九、抽象(abstract)
抽象方法
只声明了方法,同时使用了abstract修饰,没有方法体!必须要子类来重写!
抽象类
包含抽象方法的类,必须设计成抽象类!
抽象类的使用要点
1. 通过抽象类,可以避免子类设计的随意性。
2. 有抽象方法的类只能定义为抽象类
3. 抽象类不能实例化,及不能用new来实例化抽象类。
4. 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用,也可以包括普通方法
5. 抽象类只能用来继承。
6. 抽象方法必须被子类实现。
课堂代码
package cn.bjsxt.oop.abstractClass;

public abstract class Animal {
String str;
public abstract void run(); //抽象方法的意义在于:将方法的设计和实现分离!
public void breath(){
System.out.println(“呼吸!”);
run(); //多态
}
public Animal(){
System.out.println(“创造一个动物!”);
}
}

class Cat extends Animal {
public void run(){
System.out.println(“猫步小跑”);
}
}
class Dog extends Animal {
public void run(){
System.out.println(“狗跑”);
}
}
package cn.bjsxt.oop.abstractClass;
public class Test {
public static void main(String[] args) {
Animal a = new Cat();
a.breath();
}
}

四十、接口(interface)

普通类:只有具体实现
抽象类:具体实现和规范(抽象方法) 都有!
接口:只有规范!

为什么需要接口?
为什么需要接口?接口和抽象类的区别?
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离。
抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接口是完全面向规范的,规定了一批类具有的公共方法规范。
从接口的实现者角度看,接口定义了可以向外部提供的服务。
从接口的调用者角度看,接口定义了实现者能提供那些服务。
接口是两个模块之间通信的标准,通信的规范。如果能把你要设计的系统之间模块之间的接口定义好,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现了。大家在工作以后,做系统时往往就是使用“面向接口”的思想来设计系统。

如何定义接口?
格式:
[访问修饰符] interface 接口名 [extends 父接口1,父接口2…] {
常量定义
方法定义
}
访问修饰符:只能是public或默认。
接口名:和类名采用相同命名机制
extends:接口可以多继承
常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
方法:接口中的方法只能是:public abstract。 省略的话,也是public abstract.
接口的本质探讨
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计 模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
接口使用要点
1. 子类通过implements来实现接口中的规范
2. 接口不能创建实例,但是可用于声明引用变量类型。
3. 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
4. 接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
5. 常量必然是:public static final。 我们不加这些修饰符,也是这样。
6. 方法必然是:public abstrac。 我们不加这些修饰符,也是这样。
7. 接口完全支持多继承。和类的继承类似,子接口扩展某个父接口,将会获得父接口中所定义的一切。
课堂代码
package cn.bjsxt.oop.testInterface;
public interface MyInteface {
//接口中只有常量和抽象方法
/public static final/ String MAX_GREAD = “BOSS”;
int MAX_SPEED = 120;
/public abstract/ void test01();
public int test02(int a,int b);
}

package cn.bjsxt.oop.testInterface;

public interface Flyable {
int MAX_SPEED = 11000;
int MIN_HEIGHT = 1;
void fly();
}

interface Attack {
void attack();
}

class Plane implements Flyable{
@Override
public void fly() {
System.out.println(“飞机靠发动机飞!”);
}
}

class Man implements Flyable{
@Override
public void fly() {
System.out.println(“跳起来,飞!”);
}
}

class Stone implements Flyable, Attack{
int weight;
@Override
public void fly() {
System.out.println(“被人扔出去,飞!”);
}

@Override
public void attack() {
System.out.println(“石头攻击!”);
}
}
package cn.bjsxt.oop.testInterface;

public class Test {
public static void main(String[] args) {
Flyable f = new Stone();
f.fly();
}
}

package cn.bjsxt.oop.testInterface;
//测试接口的多继承
public interface InterfaceA {
void aaa();
}

interface InterfaceB {
void bbb();
}
interface InterfaceC extends InterfaceA,InterfaceB {
void ccc();
}

class TestClass implements InterfaceC{

@Override
public void aaa() {
// TODO Auto-generated method stub
}

@Override
public void bbb() {
// TODO Auto-generated method stub
}
@Override
public void ccc() {
// TODO Auto-generated method stub
}
}
课堂代码(测试回调,callback,亦称钩子函数)
package cn.bjsxt.oop.callback;
public class PaintFrame {
public static void drawFrame(IMyFrame f){
System.out.println(“启动线程”);
System.out.println(“增加循环”);
System.out.println(“查看消息栈”);
//画窗口
f.paint();
System.out.println(“启动缓存,增加效率”);
}
public static void main(String[] args) {
drawFrame(new GameFrame01());
}
}

class GameFrame01 /extends MyFrame/ implements IMyFrame {
public void paint(){
System.out.println(“GameFrame01.paint()”);
System.out.println(“画窗口”);
}
}

class GameFrame02 extends MyFrame {
public void paint(){
System.out.println(“GameFrame02.paint()”);
System.out.println(“画窗口”);
}
}
package cn.bjsxt.oop.callback;
public abstract class MyFrame {
public void paint(){
System.out.println(“把自己窗口画出来!”);
}
}
interface IMyFrame {
void paint();
}
四十一、组合
使用组合,可以获得更多的灵活性,你甚至可以在运行的时候才决定把哪几个类组合在一起。
使用继承,他是一种高度的耦合,派生类和基类被紧紧的绑在一起,灵活性大大降低,而且,滥用继承,也会使继承树变得又大又复杂,很难理解和维护
如果是”is-a”关系,用继承。
如果是”has-a”关系,用组合。
class Computer {
Cpu cpu;
Mainboard md;
Mouse mouse;
}
class Cpu {
}
class Mainboard{
}
class Mouse {
}

四十二、内部类(innerclasses)
一般情况,我们把类定义成独立的单元。有些情况下,我们把一个类放在另一个类的内部定义,称为内部类。

普通的成员内部类可以访问 外部类普通的属性方法,可以看作外部内的普通成员;内部类对象寄存于外部类对象(Face.Nose nose = new Face().new Nose();)
静态内部类只可以访问外部类的静态属性和方法,可以看作外部类的静态成员;静态内部类存在时,外部类对象不一定存在(Face.Ear ear = new Face.Ear();)

内部类的作用:
1. 内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
2. 内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类不能访问内部类的内部属性。
内部类的使用场合:
由于内部类提供了更好的封装特性,并且可以很方便的访问外部类的属性。所以,通常内部类在只为所在外部类提供服务的情况下优先使用。

内部类的分类:
1. 成员内部类(可以使用private、proteted、public任意进行修饰。 类文件:外部类$内部类.class)
a) 非静态内部类(外部类里使用非静态内部类和平时使用其他类没什么不同)
i. 非静态内部类必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。 非静态内部类对象单独属于外部类的某个对象。
ii. 非静态内部类可以使用外部类的成员,但是外部类不能直接访问非静态内部类成员。
iii. 非静态内部类不能有静态方法、静态属性、静态初始化块。
iv. 静态成员不能访问非静态成员:外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
v. 成员变量访问要点:
1. 内部类里方法的局部变量:变量名
2. 内部类属性:this.变量名
3. 外部类属性:外部类名.this.变量名
vi. 内部类的访问:
1. 外部类中定义内部类: new InnerClass()
2. 外部类以外的地方使用非静态内部类:
Outer.inner varname = OuterObject.new Inner()
Face.Nose nose = new Face().new Nose();
Face f2 = new Face();
Face.Nose nose2 = f2.new Nose();
b) 静态内部类
i. 定义方式:
static class ClassName {
//类体
}
ii. 使用要点:
1. 当一个静态内部类对象存在,并不一定存在对应的外部类对象。 因此,静态内部类的实例方法不能直接访问外部类的非静态的方法和属性。
2. 静态内部类看做外部类的一个静态成员。 因此,外部类的方法中可以通过:静态内部类.名字 访问静态内部类的静态成员。通过 new 静态内部类()访问静态内部类的实例。
3. 在外部类的外面创建静态内部类:
Face.TestStaticInner aInner = new Face.TestStaticInner();
2. 匿名内部类
适合那种只需要使用一次的类。比如:键盘监听操作等等。语法:
new 父类构造器(实参类表) 实现接口 () {
//匿名内部类类体!
}

    this.addWindowListener(new WindowAdapter(){

@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}

    });

this.addKeyListener(new KeyAdapter(){
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
myTank.keyPressed(e);
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
myTank.keyReleased(e);
}
});

局部内部类
定义在方法内部。作用域只限于本方法。用的非常少。
四十三、数组概述和特点
数组的定义:数组是相同类型数据的有序集合. 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们.
n 数组的四个基本特点:
1. 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
2. 其元素必须是相同类型,不允许出现混合类型。
3. 数组中的元素可以是任何数据类型,包括基本类型和引用类型。
4. 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
创建数组和初始化
n 声明(一维数组的声明方式有两种)
type[] arr_name;(推荐使用这中方式)
type arr_name[];
注:
1. 声明的时候并没有实例化任何对象,只有在实例化数组对象时,JVM才分配空间,这时才与长度有关。因此,声明数组时不能指定其长度(数组中元素的个数),例如: int a[5]; //非法
2. 声明一个数组的时候并没有数组被真正的创建。
3. 构造一个数组,必须指定长度。
n 构建数组:
创建基本类型一维数组示例:
public class Test{
public static void main(String args[]){
int[] s = null; // 1
s = new int[10]; //2
for ( int i=0; i<10; i++ ) {
s[i] =2*i+1;
System.out.println(s[i]);
} //3
}
}

创建引用类型一维数组示例:
class Man{
private int age;
private int id;
public Man(int id,int age) {
super();
this.age = age;
this.id = id;
}
}

public class AppMain {
public static void main(String[] args) {
Man[] mans;
mans = new Man[10]; //1

Man m1 = new Man(1,11);
Man m2 = new Man(2,22); //2
mans[0]=m1;
mans[1]=m2; //3
}
}

n 初始化
1. 静态初始化
除了用new关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素分配空间并赋值。
int[] a = {1,2,3};
Man[] mans = {
new Man(1,1),
new Man(2,2)
};
2. 动态初始化
数组定义与为数组元素分配空间并赋值的操作分开进行。
int[] a = new int[2];
a[0]=1;
a[1]=2;

3.数组的默认初始化:
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

int a[] = new int[2]; //0,0
boolean [] b = new boolean[2]; //false,false
String[] s = new String[2]; //null, null
3. 数组的界限

  1. 下标的合法区间:[0, length-1]

课堂代码
package cn.bjsxt.array;
public class Car {
String name;
public Car(String name){
this.name= name;
}
}
package cn.bjsxt.array;
/**

  • 数组的基本概念
  • @author dell

/
public class Test01 {
public static void main(String[] args) {
/
*

    1. 数组是相同数据类型(数据类型可以为任意类型)的有序集合
    1. 数组也是对象。数组元素相当于对象的成员变量(详情请见内存图)
    1. 数组长度的确定的,不可变的。如果越界,则报:ArrayIndexOutofBoundsException
      */
      int[] a = new int[3];
      a[0] = 23;
      a[1] = 28;
      a[2] = 32;
      Car[] cars = new Car[4];
      cars[0] = new Car(“奔驰”);
      System.out.println(a.length);
      for(int i=0;i<a.length;i++){
      System.out.println(a[i]);
      }
      }
      }

四十四、字符串(java.lang.String类)的使用
Java字符串就是Unicode字符序列,例如串“Java”就是4个Unicode字符J,a,v,a组成的。
n Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是String类的一个实例。
String e = “” ; // an empty string
String greeting = “Hello World”;
n Java允许使用符号"+"把两个字符串连接起来
String s1 = “Hello”;String s2 = “World!”;
String s = s1 + s2; //HelloWorld!
n 符号“+”把两个字符串按给定的顺序连接在一起,并且是完全按照给定的形式。
n 当“+”运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接
int age = 18;
String s = “age is” + age; //s赋值为age is 18
这种特性通常被用在输出语句中:
System.out.println(“age is” + age);
字符串类常用的方法:
n char charAt(int index)
返回字符串中第index个字符。
n boolean equals(String other)
如果字符串与other相等,返回true
n boolean equalsIgnoreCase(String other)
如果字符串与other相等(忽略大小写),则返回true
n int indexOf(String str) lastIndexOf()
n int indexOf(String str,int fromIndex)
返回与str匹配的第一个字串的开始位置,该位置从0或fromIndex开始计算,如果原始串中不存在str,返回-1.
n int length()
返回字符串的长度。
n String replace(char oldChar,char newChar)
返回一个新串,它是通过用 newChar 替换此字符串中出现的所有oldChar而生成的
n boolean startsWith(String prefix)
如果字符串以prefix开始,则返回true
n boolean endsWith(String prefix)
如果字符串以prefix结尾,则返回true
n String substring(int beginIndex)
n String substring(int beginIndex,int endIndex)
返回一个新字符串,该串包含从原始字符串beginIndex到串尾或endIndex-1的所以字符
n String toLowerCase()
返回一个新字符串,该串将原始字符串中的所有大写字母改成小写字母
n String toUpperCase()
返回一个新字符串,该串将原始字符串中的所有小写字母改成大写字母
n String trim()
返回一个新字符串,该串删除了原始字符串头部和尾部的空格
public class StringTest2 {
public static void main(String[] args) {
String s = “”;
String s1 = “How are you?”;
System.out.println(s1.startsWith(“How”));
System.out.println(s1.endsWith(“you”));
s = s1.substring(4);
System.out.println(s);
s = s1.substring(4,7);
System.out.println(s);
s = s1.toLowerCase();
System.out.println(s);
s= s1.toUpperCase();
System.out.println(s);
String s2 = " How old are you!! " ;
s = s2.trim();
System.out.println(s);
System.out.println(s2);
}
}
字符串相等的判断
n equals方法用来检测两个字符串内容是否相等。如果字符串s和t内容相等,则s.equals(t)返回true,否则返回false.
n s和t既可以是字符串变量,也可以是字符串常数,例如: “Hello”.equals(t);
n 要测试两个字符串除了大小写区别外是否是相等的,需要使用equalsIgnoreCase方法,例如:
“Hello”.equalsIgnoreCase(“hellO”);//true
n 判断字符串是否相等不要使用“
equals与
的区别
equals操作表示的两个变量是否是对同一个对象的引用,即堆中的内容是否相同
==操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地址是否相同,即栈中的内容是否相同
Object类中的equals方法是比较两个对象是否是同一个对象。
String类中的equals方法是比较两个对象中的内容是否相同。
//注意Object类中equals方法 只会比较是否为同一对象
//而子类String中的equals方法优先比较是否为同一对象, 不是同一对象会将内容拆分成char[]数组 进行按位比较
String str1 = new String(“abcd”);
String str2 = str1;
String str3 = new String(“abcd”);
String str4 = “abcd”;
String str5 = “abcd”;

	//注意equals与==的区别
	//equals:比较两者的内容
	//==:比较两者的地址

System.out.println(str1.equals(str2)); //true
System.out.println(str1.equals(str3)); //true
System.out.println(str1 == str3); //false System.out.println(str4.equals(str5)); //true
System.out.println(str4 == str5); //true
代码://总计4个对象"a",“a0”,“a01”,“a012”
String s = “a”;
for (int i = 0; i < 3; i++) {
s += i;
}//字符串叠加,尽量避免,浪费空间
String s1 = new String(“a”);//总计:两个对象"a",new String()
System.out.println(s);//a012

//总计创建两个对象
StringBuilder sb1 = new StringBuilder();
for (int i = 0; i < 3; i++) {
sb1.append(i);
}
四十五、StringBuilder与StringBuffer
StringBuilder与StringBuffer区别

StringBuilder可变字符串,线程不太安全,但效率高,推荐使用。
StringBuffer可变字符串,线程安全,但效率低,不推荐使用。
String 是不可变字符串,根据实时情况使用,在循环当中不要使用,因为会产生好的没用的字符串。

如果是简单的声明一个字符串没有后续过多的操作,使用String,StringBuilder均可,若后续对字符穿做频繁的添加,删除操作,或者是在循环当中动态的改变字符穿的长度应该用StringBuilder。使用String会产生多余的字符串,占用内存空间。

public class Test {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder(“abcdefghijklmnopqrstuvwxyz”);
sb.append(true).append(“ABC”);// append(参数)添加方法,返回值是this
System.out.println(sb);
sb.delete(3, 5);// 删除方法是[start end)这样的一个规律。
System.out.println(sb);
sb.replace(3, 5, “Hello”);// 替换方法是从start 开始 end结束之间插入字符串。
System.out.println(sb);
System.out.println(sb.length());
}

}
四十六、数组_模拟ArrayList容器的底层实现_JDK源码分析代码
package com.bjsxt.string;

import java.util.ArrayList;

public class MyArrayList {

private Object value[];
private int size;

public MyArrayList() {
	// value = new Object[16];
	this(2);
}

public MyArrayList(int size) {
	value = new Object[size];
}

public void add(Object obj) {
	value[size] = obj;
	size++;
	if (size >= value.length) {
		// 装不下了,将要扩展容量了
		int newCapacity = value.length * 2;
		Object[] newList = new Object[newCapacity];
		// System.arraycopy(src, srcPos, dest, destPos, length);
		for (int i = 0; i < value.length; i++) {
			newList[i] = value[i];
		}
		value = newList;
	}
}

public int size() {
	return size;
}

public boolean isEmpty() {
	return size == 0;
}

public int indexOf(Object obj) {
	if (obj == null) {
		return -1;
	} else {
		for (int i = 0; i < value.length; i++) {
			if (obj == value[i]) {
				return i;
			}
		}
		return -1;
	}

}

public int lastIndexOf(Object obj) {
	if (obj == null) {
		return -1;
	} else {
		for (int i = value.length - 1; i >= 0; i--) {
			if (obj == value[i]) {
				return i;
			}
		}
		return -1;// return 作用:1,返回出去值,2,结束语句。
	}

}

public void rangleCheck(int index) {
	if (index < 0 || index > size - 1) {// [0,size-1]
		try {
			throw new Exception();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

public Object set(int index, Object object) {
	rangleCheck(index);
	Object old = value[index];
	value[index] = object;
	return old;
}

public Object get(int index) {
	rangleCheck(index);
	return value[index];
}

public static void main(String[] args) {
	MyArrayList list = new MyArrayList();
	list.add("aaa");
	list.add(new Human("蒋"));
	list.add("bbb");
	list.add("bbb");
	list.add("bbb");
	list.add("bbb");
	Human h = (Human) list.get(1);
	System.out.println(h.getName());
	System.out.println("*********************");
	System.out.println(list.get(0));
	System.out.println(list.get(1));
	System.out.println(list.get(2));
	System.out.println("*******************");
	System.out.println(list.size);

}

}

class Human {
private String name;

public Human(String name) {
	this.name = name;
}

public String getName() {
	return name;
}

public void setName(String name) {
	this.name = name;
}

}
四十七、多维数组
数组常见的操作
1.数组的拷贝:system.arraycopy();
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。从 src 引用的源数组到 dest 引用的目标数组,数组组件的一个子序列被复制下来。被复制的组件的编号等于 length 参数。源数组中位置在 srcPos 到 srcPos+length-1 之间的组件被分别复制到目标数组中的 destPos 到 destPos+length-1 位置。

2.数组的排序:Arrays.sort();
3.静态打印数组:Arrays.toString();
4.按二分法查找:Arrays.binarySearch();
5.数组的填充:Arrays.fill(a,2,4,100); //将数组a的2-4索引元素替换为100

静态初始化:

int[][] a = {
{1,2,3},
{3,4},
{3,5,6,7}
};
System.out.println(a[2][3]);
内存分析:

动态初始化:
int[][] a = new int[3][];
//a[0] = {1,2,5}; //错误,没有声明类型就初始化
a[0]=new int[]{1,2};
a[1]=new int[]{2,2};
a[2]=new int[]{2,2,3,4};
System.out.println(a[2][3]);
获取数组长度:
a.length获取的二维数组第一维数组的长度,a[0].length才是获取第二维第一个数组长度。
b.冒泡排序
public class Test {
public static void main(String[] args) {
int[] values = { 3, 1, 6, 2, 9, 0, 7, 4, 5,8 };
sort(values);
System.out.println(Arrays.toString(values));
}
public static void sort(int[] values) {
int temp;
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < values.length - 1- i ; j++) {
if (values[j] > values[j + 1]) {
temp = values[j];
values[j] = values[j + 1];
values[j + 1] = temp;
}
}
}
}
}

二分法查找
import java.util.Arrays;
public class TestBinarySearch {
public static void main(String[] args) {
System.out.println(args[1]);
int[] arr = {234,245,77,3,543,67,78,95,378,678,205,753,457,2903,340} ;
int searchWord = 6780; //所要查找的数
int searchCount = 0; //循环的次数
System.out.printf(“普通循环查找%d的次数是%d”,searchWord,genetalLoop(arr,searchWord));
System.out.printf(“二分法查找%d的次数是%d”,searchWord,binarySearch(arr,searchWord));
}
static int genetalLoop(int[] arr,int searchWord){
//普通的循环法,最少需要比较一次,比如查找1,最多需要比较15次,比如8721
int searchCount = 0;
for(int i=0;i<arr.length;i++){
searchCount++;
if (searchWord==arr[i])
break;
}
return searchCount;
}
//int[] arr = {234,245,77,3,543,67,78,95,378,678,205,753,457,2903,340};
static int binarySearch(int[] arr,int searchWord){
Arrays.sort(arr); //先对传进来的数组进行排序
System.out.println("\n"+Arrays.toString(arr));
//二分法查找
int iIndex=0; //相当于指针的东西
int iStart=0;
int iEnd=arr.length-1;
int searchCount = 0;
for(int i=0;i<arr.length/2;i++) {
searchCount++;
iIndex = (iStart+iEnd)/2;
if(arr[iIndex]<searchWord){
System.out.println(“aa”);
iStart = iIndex;
}else if(arr[iIndex]>searchWord){
System.out.println(“bb”);
iEnd = iIndex;
}else{
break;
}
}
return searchCount;
}
}
命令行参数的问题
public class Test {
public static void main(String[] args) {
for ( int i = 0; i < args.length; i++ ) {
System.out.println(“args[” + i + "] = " + args[i]);
}
}
}
//运行程序Test13_1.java
java Test lisa bily “Mr Brown”
//输出结果:
args[0] = lisa
args[1] = bily
args[2] = Mr Brown
eclipse里如何使用?

增强for循环
(JDK5.0后增加的,一般用于读取数组或集合中所有的元素!)
String[] ss = {“aa”,“bbb”,“ccc”,“ddd”};
for (int i = 0; i < ss.length; i++) {
String temp = ss[i];
System.out.println(temp);
}
for (String temp : ss) {
System.out.println(temp);
}
利用二维数组实现:n阶矩阵的加法、乘法操作。
public class TestMatrix {
public static void main(String[] args){
int[][] a = {
{2,2,4,6},
{4,5,6,8},
{1,2,32,3},
{1,2,3,4}
} ;
int[][] b = {
{80,2,4,7},
{6,5,6,2},
{9,7,2,3},
{9,3,2,3}
} ;
mutiple(a,b);
}
static void mutiple(int[][] a,int[][] b){
int[][] c = new int[a.length][a.length];
for(int i=0;i<a.length;i++){
for(int j=0;j<a[i].length;j++){
for(int k=0;k<a[i].length;k++){
c[i][j] += a[i][k]*b[k][j];
}
// c[i][j] = a[i][0]*b[0][j]+a[i][1]b[1][j]+a[i][2]b[2][j];
}
}
printArray©;
}
static void add(int[][] a,int[][] b){
int[][] c = new int[a.length][a.length];
for(int i=0;i<a.length;i++){
for(int j=0;j<a[i].length;j++){
c[i][j] = a[i][j] +b[i][j] ;
}
}
printArray©;
}
static void printArray(int[][] c){
for(int i=0;i<c.length;i++){
for(int j=0;j<c[i].length;j++){
//sddfdsfds
//System.out.printf((c[i][j]<10?" “+c[i][j]:c[i][j])+” “);
System.out.printf(”%5d",c[i][j]);
}
System.out.println("");
}
}
}
四十八、基本数据类型的包装类(Wrapper Class)
包装类基本知识
JAVA并不是纯面向对象的语言。Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的。但是我们在实际使用中经常需要将基本数据转化成对象,便于操作。比如:集合的操作中。例如使用Map对象要操作put()方法时,需要传入的参数是对象而不是基本数据类型。为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。包装类均位于java.lang包,包装类和基本数据类型的对应关系如下表所示:
基本数据类型 包装类
byte Byte
boolean Boolean
short Short
char Character
int Integer
long Long
float Float
double Double
在这八个类名中,除了Integer和Character类以后,其它六个类的类名和基本数据类型一直,只是类名的第一个字母大写即可。
包装类的用途
对于包装类说,这些类的用途主要包含两种:
a、作为和基本数据类型对应的类类型存在,方便涉及到对象的操作。
b、包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法(这些操作方法的作用是在基本类型数据、包装类对象、字符串之间提供转化!)。
所有的包装类(Wrapper Class)都有共同的方法,他们是:
/

  • 测试Integer的用法,其他包装类类似
    */
    public class Wraper {
    public static void main(String[] args) {
    // 基本类型转换成Integer对象
    Integer int1 = new Integer(10);
    Integer int2 = Integer.valueOf(20);
    // Integer对象转换成int
    int a = int1.intValue();
    // 字符串转化成Integer对象
    Integer int3 = Integer.parseInt(“334”);
    Integer int4 = new Integer(“999”);
    // Integer对象转化成字符串
    String str1 = int3.toString();
    // 一些常见的int类型相关的常量。
    System.out.println(“int能表示的最大整数:” + Integer.MAX_VALUE);
    System.out.println(“int能表示的最小整数:” + Integer.SIZE);
    // 数字转字符串非常简单
    String str = 234 + “”;
    System.out.println(str);
    }

}
四十九、自动装箱和拆箱?autoboxing,unboxing
就是将基本类型和包装类进行自动的互相转换。
JDK5.0后,将自动装箱/拆箱引入java中。
自动装箱的过程:每当需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装中。
自动拆箱的过程:每当需要一个值时,被装箱对象中的值就被自动地提取出来,没必要再去调用intValue()和doubleValue()方法。
自动装箱与拆箱的功能事实上是编译器来帮您的忙,编译器在编译时期依您所编写的语法,决定是否进行装箱或拆箱动作。例如:
Integer i = 100;
相当于编译器自动为您作以下的语法编译:
Integer i = new Integer(100);
所以自动装箱与拆箱的功能是所谓的“编译器蜜糖”(Compiler Sugar),虽然使用这个功能很方便,但在程序运行阶段您得了解Java的语义。例如下面的程序是可以通过编译的:
Integer i = null;
int j = i;
这样的语法在编译时期是合法的,但是在运行时期会有错误,因为这种写法相当于:
Integer i = null;
int j = i.intValue();
null表示i没有参考至任何的对象实体,它可以合法地指定给对象参考名称。由于实际上i并没有参考至任何的对象,所以也就不可能操作intValue()方法,这样上面的写法在运行时会出现NullPointerException错误。
/**

  • 测试自动装箱和拆箱
  • 结论:虽然很方便,但是如果不熟悉特殊情况,可能会出错!
    */
    static void testBoxing(){
    Integer b = 23; //自动装箱,相当于Integer b = new Integer(23);
    int a = new Integer(20); //自动拆箱

//自动装箱和拆箱,实际上是编译器替我们完成了代码的自动编译,比如:Integer b = 23, 其实运行时执行的仍然是:Integer b = new Integer(23);
//下面的问题我们需要注意:
// Integer c = null;
// int d = c; //此处其实就是:c.intValue(),因此抛空指针异常。

//自动装箱拆箱时,对于-128-127之间的值,编译器仍然会把它当做基本类型处理。
Integer h = 100; Integer u = 100;
Integer h2 = 200; Integer u2 = 200;
if(hu){
System.out.println(“100等于”);
}
if(h2
u2){
System.out.println(“200等于”);
}
//输出结果100等于
}
自动装箱和自动拆箱的案例代码:

public class AutoBox {
public static void main(String[] args) {
//Integer i= new Integer(10);
Integer i1=100;//jdk5.0之后,编译器帮我们改进代码相当于Integer i1=new Integer(100);
Integer i2=2000;
int c=i2;//自动拆箱,编译器帮我们改进:i2.intValue();
System.out.println©;
Integer d=1234;
Integer d2=1234;
System.out.println(dd2);
System.out.println(d.equals(d2));
System.out.println("*******************");
Integer d3=-129;//[-128,127]之间的数,仍然当做基本数据类型来处理。
Integer d4=-129;
System.out.println(d3
d4);
System.out.println(d3.equals(d4));
}

}
五十、时间处理相关类
Date时间类(java.util.Date)
在标准Java类库中包含一个Date类。它的对象表示一个特定的瞬间,精确到毫秒。
Date()分配一个Date对象,并初始化此对象为当前的日期和时间精确到毫秒)。
Date(long date) 分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数,类型是long。
Boolean after(Date when) 测试此日期是否在指定日期之后。
boolean before(Date when) 测试此日期是否在指定日期之前。
Boolean equals(Object obj) 比较两个日期的相等性。
Long getTime()返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
String toString() 把此 Date 对象转换为以下形式的 String:
dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。

public class TestDate {
public static void main(String[] args) {
// 要去看看Date类的源码,可以深入理解Date类。
Date d = new Date();// 得到了当前时间的毫秒数。
long l1 = System.currentTimeMillis();
System.out.println(l1);
Date d2 = new Date(1000);
System.out.println(d2.toString());
System.out.println(d2.toGMTString());// 不建议使用。
d2.setTime(24324324);
System.out.println(d2.getTime());
System.out.println(d.getTime() < d2.getTime());
Date date1 = new Date();
System.out.println(date1.toString());
long i = date1.getTime();
Date date2 = new Date(i - 1000);
Date date3 = new Date(i + 1000);
System.out.println(date1.after(date2));
System.out.println(date1.before(date2));
System.out.println(date1.equals(date2));
System.out.println(date1.after(date3));
System.out.println(date1.before(date3));
System.out.println(date1.equals(date3));
System.out.println(
new Date(1000L * 60 * 60 * 24 * 365 * 39L).toString()); // why?
}

}
尝试一下
查看API文档大家可以看到很多方法过时了,JDK1.1之前的Date包含了:日期操作、字符串转化成时间对象,时间对象。 1.1之后,日期操作使用:Canlendar类来。 字符串转化:DateFormat。
DateFormat和SimpleDateFormat类
作用:把时间对象转化成指定格式的字符串。反之,把指定格式的字符串转化成时间对象。
DateFormat是一个抽象类。一般使用SimpleDateFormat类。
示例:

public class TestDate {
public static void main(String[] args) throws ParseException {
// new出SimpleDateFormat对象
SimpleDateFormat s1 = new SimpleDateFormat(“yyyy-MM-dd hh:mm:ss”);
SimpleDateFormat s2 = new SimpleDateFormat(“yyyy-MM-dd”);
// 将时间对象转换成字符串
String daytime = s1.format(new Date());
System.out.println(s2.format(new Date()));
System.out.println(new SimpleDateFormat(“hh:mm:ss”).format(new Date()));
// 将符合指定格式的字符串转成成时间对象.字符串格式需要和指定格式一致。
String time = “2007-10-7”;
Date date = s2.parse(time);
System.out.println("date1: " + date);
time = “2007-10-7 20:15:30”;
date = s1.parse(time);
System.out.println("date2: " + date);
}

}

格式化字符的含义:
字母 日期或时间元素 表示 示例
G Era 标志符 Text AD
y 年 Year 1996; 96
M 年中的月份 Month July; Jul; 07
w 年中的周数 Number 27
W 月份中的周数 Number 2
D 年中的天数 Number 189
d 月份中的天数 Number 10
F 月份中的星期 Number 2
E 星期中的天数 Text Tuesday; Tue
a Am/pm 标记 Text PM
H 一天中的小时数(0-23) Number 0
k 一天中的小时数(1-24) Number 24
K am/pm 中的小时数(0-11) Number 0
h am/pm 中的小时数(1-12) Number 12
m 小时中的分钟数 Number 30
s 分钟中的秒数 Number 55
S 毫秒数 Number 978
z 时区 General time zone Pacific Standard Time; PST; GMT-08:00
Z 时区 RFC 822 time zone -0800
Calendar 日历类 (将日期转为时刻)
Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,格里高利历)的偏移量。
GregorianCalendar 是 Calendar 的一个具体子类,提供了世界上大多数国家/地区使用的标准日历系统。
星期:周日是1,周一是2。。。周六是7
注意月份的表示,一月是0,二月是1,以此类推,是12月是11。因为大多数人习惯于使用单词而不是使用数字来表示月份,这样程序也许更易读,父类Calendar使用常量来表示月份:JANUARY, FEBRUARY,等等。
package com.jiang.testDate;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**

  • 测试日历类
  • @author 蒋蒋

*/
public class TestCalendar {
public static void main(String[] args) {
Calendar c = new GregorianCalendar();
// c.set(2001, 1, 12, 14, 32, 34);

	/*c.set(Calendar.YEAR, 2001);
	c.set(Calendar.MONTH, 1);
	c.set(Calendar.DATE, 12);
	c.set(Calendar.HOUR, 14);
	c.set(Calendar.MINUTE, 32);
	c.set(Calendar.SECOND, 34);*/
	
	c.setTime(new Date());
	Date d = c.getTime();
	
	System.out.println(c.get(Calendar.YEAR));
	System.out.println(d);
	
	DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	String str = df.format(d);
	System.out.println(str);
	
	System.out.println("**************");
	//测试日期计算
	c.add(Calendar.YEAR, 3);
}

}

编写程序,利用GregorianCalendar类,打印当前月份的日历,样式如下: 可视化日历的编写
今天的日期是 2008-02-23 ,如下为今日所在月份的日历

package com.jiang.testDate;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Scanner;

/**

  • 可视化日历
  • @author 蒋蒋

*/
public class VisualCalendar {
public static void main(String[] args) {
System.out.println(“请输入日期(按照格式:2013-5-18)”);
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
DateFormat df = new SimpleDateFormat(“yyyy-MM-dd”);
try {
Date date = df.parse(str);
Calendar c = new GregorianCalendar();
c.setTime(date);
int day = c.get(Calendar.DATE);
c.set(Calendar.DATE, 1);
int maxDate = c.getActualMaximum(Calendar.DATE);
System.out.println(“日\t一\t二\t三\t四\t五\t六\t”);

		for (int i = 1; i < c.get(Calendar.DAY_OF_WEEK); i++) {
			System.out.print("\t");
		}
		
		for (int i = 1; i <= maxDate; i++) {
			if(i == day){
				System.out.print("*");
			}
			System.out.print(i+"\t");
			int w = c.get(Calendar.DAY_OF_WEEK);
			if(w == Calendar.SATURDAY){
				System.out.println();
			}
			c.add(Calendar.DATE, 1);
		}
	} catch (ParseException e) {
		e.printStackTrace();
	}
}

}

五十一、File类
File类的基本用法
1 java.io.File类:文件和目录路径名的抽象表示形式。
File类的常见构造方法:
public File(String pathname)
以pathname为路径创建File对象,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
2 File的静态属性String separator存储了当前系统的路径分隔符。
3 通过File对象可以访问文件的属性。
public boolean canRead() public boolean canWrite()
public boolean exists() public boolean isDirectory()
public boolean isFile() public boolean isHidden()
public long lastModified() public long length()
public String getName() public String getPath()
4 通过File对象创建空文件或目录(在该对象所指的文件或目录不存在的情况下)。
public boolean createNewFile()throws IOException
public boolean delete()
public boolean mkdir(), mkdirs()
File f5 = new File(“C:/aa/bb/cc”);
f5.mkdir();//仅创建一个目录:aa
f5.mkdirs();//创建一个目录:aa,创建子目录:bb,创建孙目录:cc
5常见构造器,方法的演示案例:

package com.bjsxt.file;

import java.io.File;
import java.io.IOException;

public class TestFile {
/**
* File文件类 1.代表文件 2.代表目录
*
*
*/
public static void main(String[] args) {
File f = new File(“d:/src3/TestObject.java”);
File f2 = new File(“d:/src3”);
File f3 = new File(f2, “TestFile.java”);
File f4 = new File(f2, “TestFile666.java”);
File f5 = new File(“d:/src3/aa/bb/cc/dd”);
//f5.mkdirs();
f5.delete();

	try {
		f4.createNewFile();
		System.out.println("文件创建成功!");
	} catch (IOException e) {
		e.printStackTrace();
	}
	if (f.isFile()) {
		System.out.println("是一个文件!");
	}
	if (f2.isDirectory()) {
		System.out.println("是一个目录!");
	}
	if (f3.isFile()) {
		System.out.println("是一个文件奥");
	}
}

}
树状结构展现文件结构
编写一个程序,在命令行中以树状结构展现特定的文件夹及其子文件(夹),利用File类和递归知识,实现树状结构的展示,代码如下:

package com.bjsxt.file;

import java.io.File;
import java.io.IOException;

public class TestFile {
/**
* 1.File文件类 的常见构造器,常见方法的使用
* 2.递归:
* 概念:在java代码中出现,出现本身调用自己的现象,我们称之为递归。
* 组成:
* 递归体。
* 递归头。
* 注意事项:
* 递归若没有递归头,递归将是死递归了。
*
*/
public static void main(String[] args) {
File f = new File(“d:/src3”);
printFile(f, 0);
}

static void printFile(File file, int level) {
	for (int i = 0; i < level; i++) {
		System.out.print("-");
	}
	System.out.println(file.getName());
	if (file.isDirectory()) {
		File[] files = file.listFiles();
		for (File temp : files) {
			printFile(temp, level + 1);
		}
	}
}

}
五十二、枚举
枚举类型:
只能够取特定值中的一个
使用enum关键字
所有的枚举类型隐性地继承自 java.lang.Enum。(枚举实质上还是类!而每个被枚举的成员实质就是一个枚举类型的实例,他们默认都是public static final的。可以直接通过枚举类型名直接使用它们。)
强烈建议当你需要定义一组常量时,使用枚举类型
尽量不要使用枚举的高级特性,事实上高级特性都可以使用普通类来实现,没有必要引入复杂性!
package com.jiang.testEnum;
/**

  • 测试枚举类型
  • @author 蒋蒋

*/
public class TestEnum {
public static void main(String[] args) {
Week w = Week.FRIDAY; //定义一个枚举类型对象
Week w1 = Week.values()[2];//返回枚举的第三个元素

	System.out.println(w1);
	String temp = testEnum(Week.SUNDAY);
	System.out.println(temp);
}

static String testEnum(Week w){
	switch(w) {
	case SUNDAY:
		return "日";
	case MONDAY:
		return "一";
	case TUESDAY:
		return "二";
	case WEDNESDAY:
		return "三";
	case THURSDAY:
		return "四";
	case FRIDAY:
		return "五";
	case SATURDAY:
		return "六";
	}
	return "";
}

}
enum Week{
SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY
}
异常机制就是当程序出现错误,程序如何安全退出的机制。
五十三、异常(Exception)的概念
实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求、你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你要读取数据库的数据,数据可能是空的等。我们的程序再跑着,内存或硬盘可能满了。等等。
软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思是例外。这些,例外情况,或者叫异常,怎么让我们写的程序做出合理的处理。而不至于程序崩溃。
常见的错误:
1. 用户输入错误
2. 设备错误
硬件问题,比如打印机关掉、服务器问题
·3. 物理限制
磁盘满了。
4. 代码限制
数组下标越界等
设计良好的程序应该在异常发生时提供处理这些错误的方法,使得程序不会因为异常的发生而终断或产生不可预见的结果。
第一个异常示例和解析:
public static void main(String[] args) {
int i = 1/0;
gggggg
}
Exception in thread “main” java.lang.ArithmeticException: / by zero
at chuji.BubbleSort.main(BubbleSort.java:11)
五十四、异常分类
JDK 中定义了很多异常类,这些类对应了各种各样可能出现的异常事件,所有异常对象都是派生于Throwable类的一个实例。如果内置的异常类不能够满足需要,还可以创建自己的异常类。

Error
Error类层次描述了Java运行时系统内部错误和资源耗尽错误。这类错误是我们无法控制的,同时也是非常罕见的错误。所以在编程中,不去处理这类错误。
Error表明系统JVM已经处于不可恢复的崩溃状态中。我们不需要管他。
打开JDK的:java.lang.error,查看他的所有子类。
Error和Exception的区别:
我开着车走在路上,一头猪冲在路中间,我刹车。这叫一个异常。
我开着车在路上,发动机坏了,我停车,这叫错误。系统处于不可恢复的崩溃状态。发动机什么时候坏?我们普通司机能管吗?不能。发动机什么时候坏是汽车厂发动机制造商的事。
Exception
所有异常类的父类,其子类对应了各种各样可能出现的异常事件。
n Runtime Exception(unchecked Exception)
出现RuntimeException就一定是你的问题,可以不捕获,因为小心点这些异常是可以避免的。 派生于RuntimeException的异常。是一类特殊的异常,如被 0 除、数组下标超范围等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对程序可读性和运行效率影响很大。因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理)。这类异常通常是由编程错误导致的,因为只有小心点,这些异常都是可以避免的,所以在编写程序时,并不要求必须使用异常处理机制来处理这类异常,所有这类异常都继承自java.lang.RuntimeException。
注意:RuntimeException这个名字很容易让人产生错误影响。事实上,我们这里讨论的所有Error、Exception都是运行时发生的。
常见的有:

  1. ArithmeticException
    如试图除以0
    if(b!=0){
    int i = 1/b;
    }else{
    System.out.println(“不能用0做除数!!”);
    }
  2. NullPointerException
    当程序访问一个空对象的成员变量或方法,访问一个空数组的成员时发生。怎么处理?
    // TestException te = null;
    TestException te = new TestException();
    if(te!=null){
    te.test1(2);
    }
  3. ClassCastException
    void test2(Object obj){
    if(obj instanceof Man){
    Man man = (Man) obj;
    }
    }
  4. ArrayIndexOutOfBoundsException
    访问的元素下表超过数组长度
    int[] a = {1,2,3};
    int idx = 3;
    if(idx<=a.length-1){
    System.out.println(a[idx]);
    }
  5. NumberFormatException
    数字格式异常!
    String str = “1234abcf”;
    Integer i = new Integer(str);
    // Integer i2 = Integer.parseInt(str);
    n Checked Exception:
    所有不是Runtime Exception的异常,统称为Checked Exception,又被称为“已检查异常”。 这类异常的产生不是程序本身的问题,通常由外界因素造成的。为了预防这些异常产生时,造成程序的中断或得到不正确的结果,Java要求编写可能产生这类异常的程序代码时,一定要去做异常的处理。
  6. 要打开的文件不存在
    编译器将检查是否为所有已检查异常提供异常处理。
    这一类异常,我们必须捕获进行处理。
    Java语言将派生于RuntimeException类或Error类的所有异常称为“未检查异常”。
    五十五、异常的处理
    JAVA是采用面向对象的方式来处理异常的。处理过程:
  7. 抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给JRE。(jre:java运行环境,包含JVM标准实现及Java核心类库)
  8. 捕获异常:JRE得到该异常后,寻找相应的代码来处理该异常。JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。
    Thread.sleep(3000);//程序暂停3秒
    1.异常的处理办法之一,捕获异常

上面过程详细解析:
try块
try语句指定了一段代码,该段代码就是一次捕获并处理的范围。在执行过程中,当任意一条语句产生异常时,就会跳过该段中后面的代码。代码中可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做相应的处理
一个try语句必须带有至少一个catch语句块或一个finally语句块 。。
注:当异常处理的代码执行结束以后,是不会回到try语句去执行尚未执行的代码。
catch
a) 每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
b) 常用方法:
toString ( )方法,显示异常的类名和产生异常的原因
getMessage( ) 方法,只显示产生异常的原因,但不显示类名。
printStackTrace( ) 方法,用来跟踪异常事件发生时堆栈的内容。
这些方法均继承自Throwable类
c) Catch捕获异常时的捕获顺序:
i. 如果异常类之间有继承关系,在顺序安排上需注意。越是顶层的类,越放在下面。再不然就直接把多余的catch省略掉。
finally
有些语句,不管是否发生了异常,都必须要执行,那么就可以把这样的语句放到finally语句块中。
通常在finally中关闭程序块已打开的资源,比如:文件流、释放数据库连接等。
典型代码(先不要敲!!):

package com.bjsxt.Exception;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class TestException {
public static void main(String[] args) {
FileReader reader = null;
try {
reader = new FileReader(“d:/a.txt”);
char temp = (char) reader.read();
System.out.println(“读出的内容:” + temp);
} catch (FileNotFoundException e) {
System.out.println(“文件没有找到!!”);
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
System.out.println(“文件读取错误!”);
} finally {
System.out.println(" 不管有没有异常,我肯定会被执行!");
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();

		}
	}

}

}
try, catch,finally ,return 执行顺序
package com.bjsxt.Exception;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class TestException {
public static void main(String[] args) {
String str = new TestException().openFile();
System.out.println(str);

}

String openFile() {
	try {
		System.out.println("aaa");
		FileInputStream fis = new FileInputStream("d:/a.txt");
		int a = fis.read();
		System.out.println("bbb");
		return "step1";
	} catch (FileNotFoundException e) {
		System.out.println("catching !!!!!");
		e.printStackTrace();
		return "step2";// 先确定返回值,并不会直接结束运行。
	} catch (IOException e) {
		e.printStackTrace();
		return "step3";
	} finally {
		System.out.println("finally !!!!!");
		// return "fff";不要在finally中使用return.
	}
}

}

执行顺序:
1. 执行try,catch , 给返回值赋值
2. 执行finally
3. return
2、异常的处理办法之二,声明异常:throws子句
当Checked Exception产生时,不一定立刻处理它,可以再把异常Throws出去。
在方法中使用try-chatch-finally由这个方法处理异常。在一些情况下,当前方法并不需要处理发生的异常,而是向上传递给调用它的方法处理。
如果一个方法中可能产生某种异常,但是并不能确定如何处理这种异常,则应根据异常规范在方法的首部声明该方法可能抛出的异常。
如果一个方法抛出多个已检查异常,就必须在方法的首部列出所有的异常,之间以逗号隔开。
典型代码(先不敲!):
package com.bjsxt.Exception;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileTest {
public static void main(String[] args) {
try {
readFile(“d:/a.txt”);
} catch (FileNotFoundException e) {
System.out.println(“所需要的文件不存在!”);
e.printStackTrace();
} catch (IOException e) {
System.out.println(“文件读写出错误!”);
e.printStackTrace();
}
}

public static void readFile(String fileName) throws IOException {
	FileReader in = new FileReader(fileName);
	try {
		int tem = 0;
		tem = in.read();
		while (tem != -1) {
			System.out.println((char) tem);
			tem = in.read();
		}
	} finally {
		in.close();
	}
}

}

方法重写中声明异常原则

子类声明的异常范围不能超过父类声明的范围。包含如下意思:
   1.    父类没有声明异常,子类也不能;
  2.    不可抛出原有方法抛出异常类的父类或上层类 
  3.    抛出的异常类型的数目不可以比原有的方法抛出的还多(不是指个数,指类型)

代码示例:

package com.jiang.testException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.ParseException;
public class TestThrows {
public static void main(String[] args) {
}
}
class A {
public void method() throws IOException,ParseException{
}
}
class B extends A {
public void method() throws FileNotFoundException {

}

}
/class C extends A {
public void method() throws Exception{
}
}
/ //报错,子类声明异常的范围超过了父类
class D extends A {
public void method() throws IOException {
}
}
class E extends A {
public void method() throws IOException,FileNotFoundException{
}
}
class F extends A {
public void method() throws IOException,ArithmeticException {
}
}
class G extends A {
public void method() throws IOException,ParseException {
}
}

异常的处理办法之三,手动抛出异常,throw子句(了解)
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要手工创建并抛出。
在捕获一个异常前,必须有一段代码先生成异常对象并把它抛出。这个过程我们可以手工做,也可以由JRE来实现,但是他们调用的都是throw子句。
对于一个已经存在的异常类,抛出该类异常对象过程如下:
找到一个合适的异常类。
创建一个该类的对象。
将对象抛出

File f = new File(“c:/tt.txt”);
if (!f.exists()) {
try {
throw new FileNotFoundException(“File can’t be found!”);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
五十六、自定义异常
在程序中,可能会遇到任何标准异常类都没有充分的描述清楚的问题,这种情况下可以创建自己的异常类
从Exception类或者它的子类派生一个子类即可
习惯上,定义的类应该包含2个构造器:一个是默认的构造器,另一个是带有详细信息的构造器
class IllegalAgeException extends Exception {
public IllegalAgeException() {
}
public IllegalAgeException(String msg) {
super(msg);
}
}

class Person {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
if (age < 0)
throw new IllegalAgeException(“人的年龄不应该为负数”);
this.age = age;
}
public String toString() {
return "name is " + name + " and age is " + age;
}
}
public class MyExceptionTest {

public static void main(String[] args) {
Person p = new Person();
try {
p.setName(“Lincoln”);
p.setAge(-1);
} catch (IllegalAgeException e) {
e.printStackTrace();
System.exit(-1);
}
System.out.println§;
}
}

使用异常机制建议

  1. 要避免使用异常处理代替错误处理,这样会降低程序的清晰性,并且效率低下。
  2. 处理异常不可以代替简单测试— 只在异常情况下使用异常机制
  3. 不要进行小粒度的异常处理—应该将整个任务包装在一个Try语句块中
  4. 异常往往在高层处理
    总结
    ³ 一个图
    ³ 五个关键字(try, catch, finally, throws, throw)
    ³ 先逮小的,再逮大的
    ³ 异常和重写的关系
    ³ 自定义异常
    五十七、游戏项目_加载窗口_画图形_加载图片_编程中的坐标
    代码如下:

package com.bjsxt.test;

import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

/**

  • 游戏窗口类
  • @author Administrator

/
public class GameFrame extends Frame {// gui编程:awt,swing等;
/
*
* 加载 窗口
*/
public void launchFrame() {
setSize(300, 300);
setLocation(100, 100);
setVisible(true);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});

}
@Override
	public void paint(Graphics g) {
	g.drawLine(100, 100, 200, 200);
	g.drawRect(100, 100, 200, 200);
	g.drawOval(100, 100, 200, 200);
	
	Font f=new Font("宋体",Font.BOLD,100);
	g.setFont(f);
	g.drawString("我是peter老师!", 200, 200);
	
	g.fillRect(100,100, 20, 20);
	g.setColor(Color.red);
	g.fillOval(300,300, 20,20);
	}

public static void main(String[] args) {
	GameFrame gf = new GameFrame();
	gf.launchFrame();
}

}
五十八、游戏工具的基本类的与图片
package com.bjsxt.test;

import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

/**

  • 游戏窗口类
  • @author Administrator

*/
public class GameFrame extends Frame {// gui编程:awt,swing等;

Image img = GameUtil.getImage("images/sun.jpg");

/**
 * 加载 窗口
 */
public void launchFrame() {
	setSize(300, 300);
	setLocation(100, 100);
	setVisible(true);

	addWindowListener(new WindowAdapter() {
		@Override
		public void windowClosing(WindowEvent e) {
			System.exit(0);
		}
	});

}

private double x = 200, y = 200;

@Override
public void paint(Graphics g) {
	g.drawLine(100, 100, 200, 200);
	g.drawRect(100, 100, 200, 200);
	g.drawOval(100, 100, 200, 200);

	Font f = new Font("宋体", Font.BOLD, 100);
	g.setFont(f);
	g.drawString("我是蒋蒋!", 200, 200);

	g.fillRect(100, 100, 20, 20);
	g.setColor(Color.red);
	g.fillOval(300, 300, 20, 20);

	g.drawImage(img, 200, 200, null);

	x += 3;
	y += 3;

}

public static void main(String[] args) {
	GameFrame gf = new GameFrame();
	gf.launchFrame();
}

}
五十九、游戏工具的辅助工具类
package com.bjsxt.test;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;

public class GameUtil {

private GameUtil() {// 工具类一般构造器私有。
	
};

public static Image getImage(String path) {
	URL u = GameUtil.class.getClassLoader().getResource(path);
	BufferedImage img = null;
	try {
		img = ImageIO.read(u);
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return img;
}

}

package com.jiang.util;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class MyFrame extends Frame {
/**
* 加载窗口
*/
public void launchFrame(){
setSize(Constant.GAME_WIDTH, Constant.GAME_HEIGHT);
setLocation(100, 100);
setVisible(true);

	new PaintThread().start();  //启动重画线程
	
	addWindowListener(new WindowAdapter() {
		@Override
		public void windowClosing(WindowEvent e) {
			System.exit(0);
		}
	});
}
/**
 * 利用双缓冲解决闪烁问题
 */
private Image offScreenImage = null;
public void update(Graphics g) {
	if(offScreenImage == null)

offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);

	Graphics gOff = offScreenImage.getGraphics();

	paint(gOff);
	g.drawImage(offScreenImage, 0, 0, null);
}

/**
 * 定义一个重画窗口的线程类,是一个内部类
 * @author dell
 *
 */
class PaintThread extends Thread {
	
	public void run(){
		while(true){
			repaint();
			try {
				Thread.sleep(40); //1s = 1000ms
			} catch (InterruptedException e) {
				e.printStackTrace();
			}   
		}
	}
	
}

}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值