//作者:苯环大叔·明太祖
//时间:2019年10月18号
//上一次修改时间:2019年10月23号
//内容:Java基础语法
//备注:部分笔记来源于网络,稍有改动,向原作者致敬。
文章目录
Java基础语法
数据类型
整型
数据类型 | 存储大小 |
---|---|
byte(字节型) | 1字节/1B |
short(短整型) | 2字节/2B |
int(整型)(默认) | 4字节/4B |
long(长整型) | 8字节/8B |
注意:long类型数值后面最好要加上大写的L或l,别加l了,不明显。
补充:关于进制转换
int a = 0x0011;//十六进制赋值,a=17
int b = 0b1000;//Java7版本开始,二进制赋值,b=8
//不要用八进制
浮点型
数据类型 | 存储大小 |
---|---|
float(单精度) | 4字节/4B |
double(双精度)(默认) | 8字节/8B |
注意:float类型数值后面最好加上F或f。double类型数值后面可以根据个人喜好加上D或d。
补充:
a.可以使用十六进制表示浮点数值
//p表示后面是指数值,基数是 2
o.125 = 2^-3 = 0x1.0p-3
b.IEEE 754规范中溢出和出错的三种特殊值
分别是:正无穷大,负无穷大,不是一个数
Double包装类中对这三个数值有定义,源码如下
class public final class Double extends Number implements Comparable<Double>{
//正无穷大
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
//负无穷大
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
//不是一个数
public static final double NaN = 0.0d / 0.0;
}
c.NaN的妙用
上面NaN不能用来检测一个值是否是一个数,因为NaN还可能是其它值,比如负数的平方根。
可以使用Double包装类内的isNaN()方法代替
//打印false,说明该判断失效,因为所有“非数值”的值都认为是不相同的
System.out.println(0.0d / 0.0 == Double.NaN);
//使用Double包装类的方法,判断是否是“非数值”,此时打印true
System.out.println(Double.isNaN(0.0d / 0.0));
d.关于四舍五入
金融计算无法接收舍入误差,二进制系统无法表示所有数,例如1/10。此时应该使用专门给大浮点数运算的类BigDecimal。
//打印0.8999999999999999,而不是0.9
System.out.println(2.0-1.1);
字符型
char类型,强烈建议不要在程序中使用 char 类型 。
char a = 'a';
char b = 98;//98在ASCII码表中对应字符b
Java中采用Unicode编码规范,具体占用多大内存,我们要从编码规范的发展说起。
a.在Unicode之前,其实已经有了许多种不同的规范:美国的ASCII、西欧语言中的ISO8859-1、俄罗斯的KOI-8、中国的GB18030和BIG-5等。
b.全世界有上百种语言,中文把汉字编写到GB2312里,日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里,各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。
c.一开始,Unicode只是2个字节,不断地,更多的字符被编写到Unicode中。但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。ASCII编码是1个字节,而Unicode编码通常是2个字节。新的问题又出现了:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。
d.把Unicode编码转化为“可变长编码”的UTF-8编码。
补充:
Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。
Unicode的实现方式也称为Unicode转换格式(Unicode Transformation Format,简称为UTF),目前主流的实现方式有UTF-16和UTF-8。随着Unicode通用字符集的扩充,进而出现了UTF-32,但是由于占用空间太大,目前很少有系统选择使用utf-32作为系统编码。
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件。
浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器。所以你看到很多网页的源码上会有类似的信息,表示该网页正是用的UTF-8编码。
布尔类型
boolean类型,只有两个值,true和false类型,默认值为false类型。
问题:boolean类型占用多大内存空间?
a.1bit,也就是1位,理由是boolean类型的值只有true和false两种逻辑值,在编译后会使用1和0来表示,这两个数在内存中只需要1位(bit)即可存储,位是计算机最小的存储单位。
b.1Byte,也就是1字节,理由是计算机处理数据的最小单位是1个字节。
c.4Byte,也就是4字节,理由来源是《Java虚拟机规范》书中描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。这样我们可以得出boolean类型占了单独使用是4个字节,在数组中又是1个字节。
总结:
根据http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html官方文档的描述:boolean: The boolean data type has only two possible values: true and false. Use this data type for simple flags that track true/false conditions. This data type represents one bit of information, but its “size” isn’t something that’s precisely defined.(布尔类型:布尔数据类型只有两个可能的值:真和假。使用此数据类型为跟踪真/假条件的简单标记。这种数据类型就表示这一点信息,但是它的“大小”并不是精确定义的。)
可以看出,boolean类型没有给出精确的定义,具体还要看虚拟机实现是否按照规范来,所以1个字节、4个字节都是有可能的。这其实是运算效率和存储空间之间的博弈,两者都非常的重要。
变量与常量
变量的声明
变量分为全局变量和局部变量。全局变量在类中,与方法体同级,也称为属性变量或直接叫属性。局部变量存在于方法体内,生命周期较全局变量要短。但他俩的声明规则是一样的。
格式:修饰符 数据类型 变量名;
private int aa;
private transient double bb;
public boolean cc;
注意:
a.修饰符可以有多个,默认是default。
b.Java变量的基本命名法则:
1、只能以下划线、字母、美元符开头。(数字不能开头)
2、后面跟下划线、字母、美元符以及数字。
3、没有长度限制。(但也不能太长!)
4、对大小写敏感。(意思是大小写代表不同含义)
c.Java驼峰式命名法(约定俗成):
1、变量名必须为有意义的单词。
2、变量名如果只有一个单词,则小写。
3、如果有2个以及多个单词,则从第二个单词开始首字母大写。
d.实际开发中,个人是严格遵守阿里巴巴程序规范,养成良好的代码习惯。
Java关键字与保留字表:
关键字名称 | 含义 |
---|---|
abstract | 表明类或者成员方法具有抽象属性 |
assert | 用来进行程序调试 |
boolean | 基本数据类型之一,布尔类型 |
break | 提前跳出一个块 |
byte | 基本数据类型之一,字节类型 |
case | 用在switch语句之中,表示其中的一个分支 |
catch | 用在异常处理中,用来捕捉异常 |
char | 基本数据类型之一,字符类型 |
class | 类 |
const | 保留关键字,没有具体含义 |
continue | 回到一个块的开始处 |
default | 默认,例如,用在switch语句中,表明一个默认的分支 |
do | 用在do-while循环结构中 |
double | 基本数据类型之一,双精度浮点数类型 |
else | 用在条件语句中,表明当条件不成立时的分支 |
enum | 枚举 |
extends | 表明一个类型是另一个类型的子类型,这里常见的类型有类和接口 |
final | 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变 |
finally | 用于处理异常情况,用来声明一个基本肯定会被执行到的语句块 |
float | 基本数据类型之一,单精度浮点数类型 |
for | 一种循环结构的引导词 |
goto | 保留关键字,没有具体含义 |
if | 条件语句的引导词 |
implements | 表明一个类实现了给定的接口 |
import | 表明要访问指定的类或包 |
instanceof | 用来测试一个对象是否是指定类型的实例对象 |
int | 基本数据类型之一,整数类型 |
interface | 接口 |
long | 基本数据类型之一,长整数类型 |
native | 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的 |
new | 用来创建新实例对象 |
package | 包 |
private | 一种访问控制方式:私用模式 |
protected | 一种访问控制方式:保护模式 |
public | 一种访问控制方式:共用模式 |
return | 从成员方法中返回数据 |
short | 基本数据类型之一,短整数类型 |
static | 表明具有静态属性 |
strictfp | 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范 |
super | 表明当前对象的父类型的引用或者父类型的构造方法 |
switch | 分支语句结构的引导词 |
synchronized | 表明一段代码需要同步执行 |
this | 指向当前实例对象的引用 |
throw | 抛出一个异常 |
throws | 声明在当前定义的成员方法中所有需要抛出的异常 |
transient | 声明不用序列化的成员域 |
try | 尝试一个可能抛出异常的程序块 |
void | 声明当前成员方法没有返回值 |
volatile | 表明两个或者多个变量必须同步地发生变化 |
while | 用在循环结构中 |
变量的初始化
格式一:修饰符 数据类型 变量名 = 值;//定义同时初始化
格式二:变量名 = 值;//把定义好的变量直接赋值初始化
注意:
a.全局变量如果不初始化,是有默认值的,整型的为0,小数的为0.0,布尔型的为false,字符型的默认值是’\u0000’,表示每个二进制位都为0的Unicode字符,打印时什么也不打印。
b.局部变量声明后,如果想使用必须先初始化。
常量的声明与赋值
在Java中使用final关键字指示常量。关键字final表示这个变量只能被赋值一次。一旦被赋值之后,就不能够再更改了。习惯上,常量名使用全大写。
public static final String NAME = "苯环大叔";
public static final String LAST_NAME = "明太祖";
注意:
实际开发中,常在一个类或枚举中定义多个常量,其它类中使用起来也比较方便。
运算符与表达式
运算符分类
算术运算符:+,-,*,/,%,++,–
赋值运算符:=,+=,-=,*=,/=,%=
关系运算符:>,<,>=,<=,==,!=,instanceof
逻辑运算符:&&,||,!
位运算符:&,|,~,^,<<,>>,>>>
条件运算符:?:
运算符优先级
顺序优先级 | 运算符 | 运算方向 |
---|---|---|
1 | ()、[]、{}、. | 从左向右 |
2 | +(正号)、-(负号)、~、!、++、–、()(强制类型转换)、new | 从右向左 |
3 | *、/、% | 从左向右 |
4 | +(加号)、-(减号) | 从左向右 |
5 | <<、>>、>>> | 从左向右 |
6 | <、<=、>、>=、instanceof | 从左向右 |
7 | ==、!= | 从左向右 |
8 | & | 从左向右 |
9 | ^ | 从左向右 |
10 | | | 从左向右 |
11 | && | 从左向右 |
12 | || | 从左向右 |
13 | ?: | 从右向左 |
14 | =、+=、-=、*=、/=、&=、|=、^=、~=、<<=、>>=、>>>= | 从右向左 |
规律:
单目运算符>双目算术运算符>双目位移>双目关系运算符>双目位运算符>双目逻辑运算符>三目运算符>赋值运算符
应用:
a.单目运算符++和–,数值放前放后不一样。i++则先表达式后自增,++i则先自增再表达式。
int a = 1;
int b = a++;
int c = ++a;
System.out.println(b);//打印1
System.out.println(c);//打印3
b.一个数左移一位相当于乘2,右移一位相当于除2。在进行2的次幂运算时,可以考虑位运算效率高的特性提高算法整体效率。
c.>>>无符号右移时,高位用0填充。>>右移高位用符号位填充,<<左移低位用0填充。
d.判断奇偶数。a&1,结果为0,a就是偶数,结果为1,a就是奇数。
e.求平均数 (x+y)/2 这样不行,要考虑 x+y可能超过int的范围,正确的方法是 (x&y)+((x^y)>>1)
f.有两个int类型变量x、y,要求两者数字交换,不用临时变量。x ^= y; y ^= x; x ^= y;(原理:b(ab)=a)。
g.取模 a % (2^n) 等价于 a & (2^n - 1)
h.求相反数(~x+1)。
i.双目逻辑运算符&&和||具有短路功能,运算符一侧判断完就能断定整个表达式的布尔值,另一侧就不用判断,短路功能由此得来。
注意:
尽量少写复杂的表达式,要提高代码的可读性。意思就是少装逼。
类型转换与类型提升
自动类型转换:低精度到高精度,部分转换可能会发生降低精度(精度丢失)。
double>float>long>int>其它
两个数值进行二元操作时,先要将两个操作数转换为同一种类型,然后再进行计算。
如果两个操作数中有一个是double类型,另一个操作数就会转换为double类型。
否则,如果其中一个操作数是float类型,另一个操作数将会转换为float类型。
否则,如果其中一个操作数是long类型,另一个操作数将会转换为long类型。
否则,两个操作数都将被转换为int类型。
注意:
a.char类型值是按照字符对应的ASCII码与整型类型运算的。
b.int转float,long转float,long转double类型时可能会发生精度丢失,这是因为存储结构造成的,具体可参考IEEE754标准。
//byte范围在-128~+127
byte b1 = 127;
byte b2 = 1;
int b3 = b1+b2;
//short范围在-32768~+32767
short s1 = 32767;
short s2 = 1;
int s3 = s1+s2;
char c1 = 'a';
int a = a + c1;
//混合运算
byte a = 1;
short b = 2;
int c = 3;
long d = 4;
float e = 5.0f;
double f = 6.0d;
double v1 = a + b + c + d + e + f;
float v2 = a + b + c + d + e;
long v3 = a + b + c + d;
int v4 = a + b + c;
强制类型转换:高精度到低精度,可能会发生数据错乱(有效数据丢失)。
//byte范围在-128~+127
byte b1 = 1;
byte b2 = 1;
byte b3 = (byte)(b1+b2);
//short范围在-32768~+32767
short s1 = 32767;
short s2 = 1;
short s3 = (short)(s1+s2);//会发生数据错乱
注意:
a.如果试图将一个数值从一种类型强制转换为另一种类型,而又超出了目标类型的表示范围,结果就会截断成一个完全不同的值,也就是丢失有效数据。
b.在C++或Java中,不要试图让boolean与任何类型进行强转,C++是防止发生错误,Java是肯定发生错误。
流程控制—条件语句
块作用域
块(block)就是用{}括起来的若干条语句,它确定了变量的作用域。
一个块可以嵌套在另一个块中。不能在嵌套的两个块中声明同名的变量。
注意:
在C++中,可以在嵌套的块中重定义一个变量。在内层定义的变量会覆盖在外层定义的变量。这样,有可能会导致程序设计错误,因此在Java中不允许这样做。
if语句
格式:if(condition){statement}
if(a>1){
System.out.println("a大于1才执行");
}
if else语句
格式:if(condition){statement1}else{statement2}
if(a>1){
System.out.println("a大于1才执行");
}else{
System.out.println("a小于等于1才执行");
}
if else嵌套链语句
格式:
if(condition1){statement1}else if(condition2){statement2}else if(condition3){statement3}…
if(a<20){
System.out.println("a小于20才执行");
}else if(a<40){
System.out.println("20<=a<40才执行");
}else if(a<60){
System.out.println("40<=a<60才执行");
}else{
System.out.println("a大于等于60才执行");
}
switch语句
格式:
switch(表达式){
case1:语句1;break;
case2:语句2;break;
…
default:默认语句,一般填报错语句
}
switch (a){
case 10:
System.out.println("a="+a);
break;
case 20:
System.out.println("a="+a);
break;
case 30:
System.out.println("a="+a);
break;
default:
System.out.println("有一些错误发生");
}
注意:
a.用switch case能表达的,用if else一定能表达。用if else能表达的,用switch case不一定能表达。平时推荐使用if语句。
b.case标签可以是类型为char、byte、short或int的常量表达式、枚举常量,从JavaSE7开始,case标签还可以是字符串字面量。
流程控制—循环语句
while循环
格式:
while(布尔表达式){循环体代码}
while(true){
System.out.println("这是一个死循环");
}
注意:
不要忘了加上跳出循环的条件,否则死循环会导致严重的程序执行错误。break语句可以直接跳出循环。
do while循环
格式:
do{循环体代码}while(布尔表达式);
int a = 1;
do {
System.out.println(a);
a++;
}while (a<=3);
注意:
a.千万别漏了while语句括号后面的分号。
b.与while语句的区别就是dowhile语句先执行一次再判断,也就是至少执行一次循环体。
c.break语句能直接跳出循环。
for循环
格式:
for(初始状态(可不写);布尔表达式;改变状态(可不写)){循环体代码}
int sum = 0;
for (int i=1;i<=100;i++){
sum += i;
}
System.out.println("1到100的和为:"+sum);
foreach循环
该循环是for循环的增强版,在Java8才有的新特性。
格式:
for(元素类型 元素名称 : 要遍历的数组实体(集合对象)){//循环体//使用元素名称可以访问单个元素}
int[] arr = {1,2,3,4,5};
List<String> list = new ArrayList<String>();
list.add("唐太宗");
list.add("宋太祖");
list.add("元世祖");
list.add("明太祖");
for(int i:arr){
System.out.println(i);
}
for(String str:list){
System.out.println(str);
}
扫描仪与输入
Scanner类
1.next()与nextline()方法
区别:
next()方法只读取输入到第一个空格。它不能读两个由空格隔开的字符串。此外,next()在读取输入后将光标放在同一行中。(next()只读空格之前的数据,并且光标指向本行)。
nextLine()方法读取输入,包括所有空格和空格连接的字符串,除了换行(即读到行尾)。读取输入后,nextLine()将光标定位在下一行。
import java.util.Scanner;
class TestScanner{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();//输入555 abc +-*/
System.out.println(str);//打印555 abc +-*/
String str2 = scanner.next();//输入555 abc +-*/
System.out.println(str2);//打印555
}
}
2.nextXxx()指定类型系列方法
nextInt,nextBoolean,nextDouble……,意思为只能输入指定类型的数据。
Scanner scanner = new Scanner(System.in);
int i = scanner.nextInt();//只允许输入整型数字
boolean b = scanner.nextBoolean();//只允许输入布尔值
double d = scanner.nextDouble();//只允许输入双精度小数
注意:
如果输入的与对应类型不同的值,或者值的范围超过类型内存模型大小,会抛出异常:java.util.InputMismatchException。
3.hasNext()方法系列
除了hasNext(),还有hasNextInt(),hasNextBoolean,hasNextDouble……,意思为判断是否还有输入或是否还有对应类型的输入。返回值为布尔值。
Scanner scanner = new Scanner(System.in);
boolean b1 = scanner.hasNext();//输入有数据即返回true,不输入则返回false。
boolean b2 = scanner.hasNextInt();//输入整型数据即返回true,否则返回false。
boolean b3 = scanner.hasNextDouble();//输入双精度小数数据即返回true,否则返回false。
System.out.println(b1);
System.out.println(b2);
System.out.println(b3);
4.Scanner类也能读取文件内容,可参考IO流操作。
格式化输出
print()与println()函数
print为一般的标准输出、println为一般的标准输出最后输出一个换行(自动换行)。
print()加了转义符\n也可以换行。打印等同于println()。
System.out.print("666");
System.out.println("777");
System.out.print("888\n");
printf()函数
printf()为格式化输出,自Java5开始,沿用C语言库函数中的printf()函数。
double x = 10000.0/3;
//占位符%f绑定
System.out.printf("x的值为:%f\n",x);//打印x的值为:3333.333333
//值占9个字符空间,保留两位小数,位数不足的,前面补空格
System.out.printf("x的值为:%9.2f\n",x);//打印x的值为: 3333.33
//整数位以逗号隔开
System.out.printf("x的值为:%,8.2f\n",x);//打印x的值为:3,333.33
常用的占位符还有:
占位符 | 意义 |
---|---|
%s | 字符串 |
%c | 字符 |
%b | 布尔值 |
%h | 哈希散列码 |
%% | 百分号 |
%n | 换行 |
函数与方法
函数的定义
格式:
修饰符 返回值类型 方法名(参数类型1 形参1,……) 抛出的异常{方法体}
class Test{
public static void findPeople(int id,String password) throws Exception{
System.out.println(id+""+password);
}
public String getPassword(){
return password;
}
}
函数的调用
格式:
1.本类调用,直接调用。
findPeople(1,123456);//无返回值
String password = getPassword();//有返回值
2.对象调用,用对象调用。
class Demo{
Test test = new Test();
test.findPeople(1,123456);
String password = test.getPassword();
}
3.类调用,静态方法直接使用类调用(类加载机制)。
class Demo{
Test.findPeople(1,123456);
}
注意:
a.函数名要遵守驼峰命名法规范,尽量用有意义的单词命名,只有一个单词全小写,大于1个单词则从第二个单词开始首字母大写。
b.调用有参函数时,由于有重载机制,传递的实际参数必须要跟调用的函数的形参顺序、类型、个数一致。
c.关于传参时的值传递机制,Java传递普通类型都是传递值的副本,传递对象类型是传递的引用的副本,但指向的是同一个对象,类似于C++的指针。
函数的递归
1.一个函数调用了它本身。
2.大问题分解为很多小问题,小问题跟大问题解决办法相同。这时候可以使用递归。
3.出现无穷递归,需要为函数添加一个出口(递归头)。
注意:
优点:程序简单
缺点:如果递归不能使问题简化并最终收敛到基础情况,就有可能出现无限递归,会导致一个 StackOverflowError(栈溢出)的错误。递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时会比循环慢的多。
使用注意:当递归方法能更自然地反映问题、易于理解和调试、并且不强调效率问题时,可以采用递归。要求高效能的情况下尽量避免使用递归。
递归与迭代的区别:
a.递归常被用来描述以自相似方法重复事物的过程,在数学和计算机科学中,指的是在函数定义中使用函数自身的方法。(A调用A)。迭代是重复反馈过程的活动,每一次迭代的结果会作为下一次迭代的初始值。(A重复调用B)
b.递归是一个树结构,从字面可以其理解为重复“递推”和“回归”的过程,当“递推”到达底部时就会开始“回归”,其过程相当于树的深度优先遍历。迭代是一个环结构,从初始状态开始,每次迭代都遍历这个环,并更新状态,多次迭代直到到达结束状态。
c.迭代可以转换为递归,但递归不一定能转换为迭代。
应用:
1.求1到100累加和。2.求阶乘。3.斐波那契额数列。4.汉诺塔。5.兔子问题。
//递归求阶乘
public static int factorial(int a){
if (a == 1){
return 1;
}
return a*factorial(a-1);
}
//迭代求阶乘
public static int factorial2(int a){
int result = 1;
for (int i=2;i<=a;i++){
result *= i;
}
return result;
}
//斐波那契数列:数列从第三项开始,每一项都等于前两项之和。如1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,…
//数学定义(递归):F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*)
class Fibonacci{
public static void main(String[] args) {
System.out.println(factorial(4));
System.out.println(iteration(4));
}
//递归求斐波那契数列
public static int factorial(int n){
if (n == 0||n == 1){
return n;
}else {
return factorial(n-1)+factorial(n-2);
}
}
//迭代求斐波那契数列
public static int iteration(int n){
int first = 0;
int second = 1;
int result = 0;
if (n == 0||n == 1){
return n;
}else {
for (int i=2;i<=n;i++){
result = first + second;
first = second;
second = result;
}
return result;
}
}
}
//汉诺塔问题
class Hannoi {
public static void main(String args[]){
System.out.println("请输入要移动的块数:");
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
move(n,'a','b','c');
}
public static void move(int n,char a,char b,char c){
if(n==1){ //当n只有1个的时候直接从a移动到c
System.out.println(a+"-->"+c);
}else{
move(n-1,a,c,b);
System.out.println(a+"-->"+c);
move(n-1,b,a,c);//第n-1个移动过来之后b变开始盘,b通过a移动到c
}
}
}
函数的回调
把函数当做函数的参数来调用。调用参数的时候,相当于调用函数。这就是函数的回调。
由于在Java语言中无法传递函数的机制,接口就成了传递函数的工具。在Java8开始引入了函数式编程,Java语言进一步优化。
1.同步回调
同步回调即,从主线程自调用回调函数起,要等待回调函数执行完,然后继续执行主线程任务。
public class Tools {
/**
* 测试函数使用时间,通过定义CallBack接口的execute方法
* @param callBack
*/
public void testTime(CallBack callBack) {
//测试起始时间
long begin = System.currentTimeMillis();
//进行回调操作
callBack.execute();
//测试结束时间
long end = System.currentTimeMillis();
//进行回调操作
System.out.println("[use time]:" + (end - begin));
}
public static void main(String[] args) {
Tools tool = new Tools();
tool.testTime(new CallBack(){
//定义execute方法
@Override
public void execute(){
//这里可以加放一个或多个要测试运行时间的方法
for (int i=1;1<10000;i++){
System.out.println("耗时间的操作");
}
}
});
}
}
interface CallBack{
//执行回调操作的方法
void execute();
}
2.异步回调
异步回调意味着新线程的开辟,主线程不必等待调用的回调函数执行完,创建完新线程和任务后,自己向下继续执行。可以给回调函数设定消息通知机制主线程执行完毕。
class CallBack {
//测试回调,主线程
public static void main(String[] args) {
myNeed();
}
public static void myNeed(){
//开启线程--->点外买
new Thread(new Runnable() {
@Override
public void run() {
//带什么外卖
String food = "你好,请帮点一份蛋炒饭盖饭,要辣,要放鸡精,不放味精,盐要低钠盐,还要多放点葱花";
//下单
takeouts(food);
}
}).start();
//玩游戏去
playGames();
}
public static void playGames() {
System.err.println("我玩游戏去了");
}
/**
* 外卖送到门外,敲门提示我外卖到了,叫我去拿外卖----这个方法就是所谓的--->回调函数
*/
public static void callback(String message) {
//这里就是需要等待之后才能进行的后续业务逻辑
System.err.println(message);
}
//外卖耗费时间与送到通知
public static void takeouts(String food) {
// 模拟带外卖需要的时间
for (int i = 0; i < 10000; i++) {
for (int j = 0; j < 100; j++) {
for (int k = 0; k < 10000; k++) {
for (int l = 0; l < 100; l++) {
}
}
}
}
// 外卖送到了门口,敲门
String message = "你的外卖到了";
callback(message);
}
}
3回调函数的应用
Spring框架的AOP编程思想。
4.代理模式和回调函数的区别:
a.代理模式需要创建接口实现类,并放入代理类中,隔离性更好,扩展性好。
b.回调函数不需要创建接口实现类,编写方便。
方法的重载
一个类中可以定义有相同的名字,但参数不同的多个方法。调用时,会根据不同的参数表选择对应的方法。
只有返回值不同不构成方法的重载。只有形参的名称不同,不构成方法的重载。
注意:要区分方法的重写(覆盖)。
class Test{
public void getPeople(String name,int id){}
public void getPeople(String name,int id,int age){}
}
面向过程编程思想
函数是最基本单位,整个程序是由一个个函数调用组成。
面向对象编程思想
类是最基本单位,方法是属于类和对象的。