为什么要用模板建站,网站建设初步策划方案,推广营销策划方案,最有创意的logo设计目录 一、Java主函数解析
二、Java学习注意事项
三、JDK和JRE的区别
1、jdk--开发环境#xff08;核心#xff09;
2、jre--运行环境
3、JVM——转换环境
四、常见转义字符
五、变量
1.变量介绍
2.变量细节
3.作用域
4.数据类型
#xff08;1#xff09;Java数…目录 一、Java主函数解析
二、Java学习注意事项
三、JDK和JRE的区别
1、jdk--开发环境核心
2、jre--运行环境
3、JVM——转换环境
四、常见转义字符
五、变量
1.变量介绍
2.变量细节
3.作用域
4.数据类型
1Java数据类型图解
2整数类型
3浮点类型
4字符类型
5布尔类型
6基本数据类型转换
六、Java中运算符的使用
1.“”号的使用
2.算术运算符
3.逻辑运算符
4.位运算符
七、键盘输入语句
八、控制结构
九、数组
1.动态初始化
2.静态初始化
3.数组赋值
4.多维数组-二维数组
十、排序
冒泡排序
十一、面向对象之类与对象
1、面向对象简述
2、类与对象的基本概念
3、类与对象的定义和使用
4、对象引用传递初步分析
5、方法的重载
6、可变参数
7、构造器
8、this关键字
9、访问修饰符
10、封装
11、继承
12、supper关键字
13、多态
十二、引用类型转换
十三、抽象类
十四、接口
十五、抽象类和接口的区别
十六、递归
一、例题 一、Java主函数解析
主函数的一般写法如下
public static void main(String[] args){…}
public static void main(String args[]){…}
下面分别解释这些关键字的作用 1public关键字这个好理解声明主函数为public就是告诉其他的类可以访问这个函数。 2static关键字告知编译器main函数是一个静态函数。也就是说main函数中的代码是存储在静态存储区的即当定义了类以后这段代码就已经存在了。如果main()方法没有使用static修饰符那么编译不会出错但是如果你试图执行该程序将会报错提示main()方法不存在。因为包含main()的类并没有实例化即没有这个类的对象所以其main()方法也不会存。而使用static修饰符则表示该方法是静态的不需要实例化即可使用。 3void关键字表明main()的返回值是无类型。 4) 参数args的主要作用是为程序使用者在命令行状态下与程序交互提供了一种手段。此外在其他类中直接使用main()函数并传递参数也是可行的虽然这种方法不太常用但毕竟为我们提供了一种选择。 二、Java学习注意事项 1、Java文件名要与主类名一致
三、JDK和JRE的区别 JDKjava development kit java开发工具 JREjava runtime environment java运行时环境 JVMjava virtuak machine java虚拟机 1、jdk--开发环境核心 Java development kit的缩写意思是Java开发工具我们写文档做PPT需要office 办公软件开发当然需要开发工具了说到开发工具大家肯定会想到Eclipse但是如果直接安装Eclipse你会发现它是运行不起来 是会报错的只有安装了JDK配置好了环境变量和path才可以运行成功。这点相信很多人都深有体会。 jdk主要包含三个部分 第一部分是Java运行时环境JVM 第二部分是Java的基础类库这个类库的数量还是相当可观的 第三部分是Java的开发工具它们都是辅助你更好地使用Java的利器 2、jre--运行环境 ① jdk中的jre 如下图jdk中包含的jre在jre的bin目录里有个jvm.dll既然JRE是运行时环境那么运行在哪肯定是JVM虚拟机上了。另jre的lib目录中放的是一些JAVA类库的class文件已经打包成jar文件。 ② 第二个JRE独立出来的运行时环境 如下图不管是JDK中的JRE还是JRE既然是运行时环境必须有JVM。所以JVM也是有两个的。、 3、JVM——转换环境 java virtuak machine java虚拟机的缩写。 大家一提到JAVA的优点就会想到一次编译随处运行说白了就是跨平台性好这点JVM功不可没。 Java的程序也就是我们编译的代码都会编译为class文件class文件就是在jvm上运行的文件只有JVM还不能完全支持class的执行因为在解释class的时候JVM需要调用解释所需要的类库lib而jre包含lib类库。 JVM屏蔽了与具体操作系统平台相关的信息使得Java程序只需生成在Java虚拟机上运行的目标代码字节码就可以在多种平台上不加修改的运行。 JVM也是一门很深的学问感兴趣的同学可以深入研究只有好处没有坏处。 其实有时候面试官问JDK和JRE的区别的目的不是想让你解释什么名词的而是想看看你的基础和研究Java的深浅还有另一方面就是你是不是经常喜欢问为什么。 四、常见转义字符 转义字符ASCII码值[十进制] 意义 \b008 退格BS将当前位置移到前一列 \f012 换页FF将当前位置一道下页开头 \n010 换行LF将当前位置移到下一行开头 \r013 回车CR将当前位置移到本行开头 \t009 水平制表HT跳到次啊一个TAB位置 \\092 代表一个反斜字符\ \’039 代表一个单引号撤号字符 \ddd三位八进制 1到3位八进制数所代表的任意字符 \xhh十六进制 十六进制所代表的任意字符
五、变量 1.变量介绍 1变量的概念 变量相当于内存中一个数据存储空间的表示你可以把变量看做是一个房间的门牌号通过门牌号我们可以找到房间而通过变量名可以访问到变量值。 2.变量细节
1变量必须先声明后使用不然会报错。 2变量表示内存中的一个存储区域[不同的变量类型不同占用的空间大小不同比如: int 4个字节double就是8个字节,先有基本印象后面说字节] 3该区域有自己的名称[变量名]和类型[数据类型] 4变量必须先声明后使用,即有顺序 5该区域的数据/值可以在同一类型范围内不断变化5.变量在同一个作用域内不能重名 6变量变量名值数据类型这一点请大家注意。变量三要素
3.作用域
3.1 基本使用
(1)在Java编程中主要的变量就是属性(成员变量)和局部变量。
(2)我们说的局部变量一般是指在成员方法中定义的变量。
(3)java中的作用域的分类
全局变量也就是属性作用域为整个类体。
局部变量也就是除了属性之外的其他变量作用域为定义它的代码块中
(4)全局变量可以不赋值直接使用因为有默认值( 0.0 )局部变量必须赋值后才能使用因为没有默认值。 3.2 注意事项和细节使用
(1)属性和局部变量可以重名访问时遵循就近原则。
(2)在同一个作用域中比如在同一个成员方法中两个局部变量不能重名。
(3)属性生命周期较长伴随着对象的创建而创建伴随着对象的死亡而死亡。局部变量生命周期较短伴随着它的代码块的执行而创建伴随着代码块的结束而死亡即在一次方法调用过程中。
(4)作用域范围不同
全局变量/属性可以被本类使用或其他类使用(通过对象调用)。
局部变量只能在本类中对应的方法中使用。
(5)修饰符不同
全局变量/属性可以加修饰符。
局部变量不可以加修饰符。
4.数据类型
每一种数据都定义了明确的类型在内存中分配了不同大小的内存空间字节。
1Java数据类型图解 2整数类型
整型的使用细节IntDetail.java 1. Java各整数类型有固定的范围和字段长度不受具体OS[操作系统]的影响以
保证java程序的可移植性。 2. Java的整型常量默认为int型,声明long型常量须后加“l”或“L”。 3. java程序中变量常声明为int型除非不足以表示大数才使用long。 4. bit:计算机中的最小存储单位。byte:计算机中基本存储单元, 1byte 8 bit。
[二进制再详细说,简单举例一个byte 3和short 3 ]
3浮点类型
基本介绍
Java的浮点类型可以表示一个小数比如123.47.80t12等等 浮点型的分类 类型 占用存储空间 范围 单精度float 4字节 -3.403E38~ 3.403E38 双精度double 8字节 -1.798E308~ 1.798E308
说明一下
1.关于浮点数在机器中存放形式的简单说明,浮点数符号位指数位尾数位
2.尾数部分可能丢失造成精度损失(小数都是近似值)。 浮点类型使用细节
浮点型使用细节 FloatDetail.java 1与整数类型类似Java浮点类型也有固定的范围和字段长度不受具体OS的 影响。[float 4个字节double是8个字节]】 2. Java的浮点型常量(具体值)默认为double型声明float型常量,须后加‘f或‘F 3浮点型常量有两种表示形式 十进制数形式:如:5.12512.0f.512(必须有小数点) 科学计数法形式:如:5.12e2 [5.12*10的2次方]5.12E-2[5.12/10的2次方 public class FloatDetail {//编写一个main方法public static void main(String[] args){//Java的浮点型常量具体值默认为double型声明float型常量须后加f或F//float num1 1.1; //错误float num2 1.1F; //正确double num3 1.1; //正确double num4 1.1f; //正确//十进制数形式如5.12 512.0f .512 (必须有小数点)double num5 .123; //等价 0.123System.out.println(num5);//科学计数法形式如5.12e2 [ 5.12 * 10的2次方 ] 5.12E-2 [ 5.12 * 10的-2次方]System.out.println(5.12e2); //512.0System.out.println(5.12E-2); //0.0512}
} 4、通常情况下应该使用double型因为它比float型更精确。
[举例说明]
double num9 2.1234567851;
输出结果 2.1234567851 float num10 2.1234567851F;
输出结果 2.1234567 5.浮点数使用陷阱:2.7和8.1/3比较 //浮点数使用陷阱:2.7和8.1/3比较double num11 2.7;double num12 8.1 / 3;System.out.println(num11); //2.7System.out.println(num12); //2.6999999999999997//得到一个重要的使用注意点当我们对运算结果是小数的进行相等判断时候要小心//应该是以两个数的差值的绝对值在某个精度范围判段//错误的写法if( num11 num12) {System.out.println(相等);}//正确的写法if(Math.abs(num11 - num12) 0.000001) {System.out.println(相等);System.out.println(Math.abs(num11 - num12));}4字符类型
基本介绍
字符类型可以表示单个字符,字符类型是char, char是两个字节(可以存放汉字)多个字符我们用字符串String(我们后面详细讲解String) 字符类型使用细节 1.字符常量是用单引号( )括起来的单个字符。例如: char c1 a;
char c2 中;
char c3 9; 2.Java中还允许使用转义字符 \ 来将其后的字符转变为特殊字符型常量。
例如:char c3 \n ; // \n 表示换行符。 3.在java中char的本质是一个整数在输出时,是unicode码对应的字符。Unicode编码转换 - 站长工具 4.可以直接给char赋一个整数,然后输出时会按照对应的unicode字符输出。
[97-》a] 5. char类型是可以进行运算的,相当于一个整数因为它都对应有Unicode码。
样例如下所示
public class CharDetail {public static void main(String[] args) {char c1 97;System.out.println(c1); //aSystem.out.println((int)c1); //97// char类型可以进行运算相当于一个整数因为它都对应有Unicode码。System.out.println(a 10); //107char c5 b 1;System.out.println((int)c5); //99System.out.println(c5); //c}
} 字符类型本质探讨
1.字符型存储到计算机中需要将字符对应的码值整数)找出来比如a
存储:a码值97二进制(110 0001) 存储
读取:二进制(110 0001)97 a显示 2.字符和码值的对应关系是通过字符编码表决定的(是规定好)
介绍一下字符编码表[sublime测试]
ASCl (ASCIl编码表一个字节表示一个128个字符,实际上一个字节可以表示256个字符,只用128个)lInicede (Unicode编码表固定大小的编码使用两个字节来表示字符字母和汉字统一都是占用两个字节,这样浪费空间) utf-8(编码表大小可变的编码字母使用1个字节汉字使用3个字节) gbk(可以表示汉字而且范围广字母使用1个字节,汉字2个字节) gb2312(可以表示汉字,gb2312 gbk) big5码(繁体中文,台湾香港)
5布尔类型
基本介绍
1布尔类型也叫boolean类型booolean类型数据只允许取值true和false无null 2.boolean类型占1个字节。 3. boolean类型适于逻辑运算一般用于程序流程控制[这个后面会详细介绍]:√if件控制语句;
√ while循环控制语句;
√ do-while循环控制语句;
√ for循环控制语句
public class Boolean01 {public static void main(String[] args) {boolean ispass true;if(ispass) {System.out.println(恭喜你考试通过);} else {System.out.println(很遗憾这次没有通过~);}}
} 使用细节说明
不可以用0或者1以及非零的数来代替false和true这点和C语言截然相反。
6基本数据类型转换
自动类型转换
√ 介绍
当java程序在进行赋值或者运算时精度小的类型自动转换为精度大的数据类型,这个就是自动类型转换。
√ 数据类型按精度(容量)大小排序为(背, 规则) public class AutoConvert {public static void main(String[] args) {//自动转换int num a; //ok char - intdouble d1 80; //ok int - doubleSystem.out.println(num); //97System.out.println(d1); //80.0}
}
自动类型转换注意和细节
1有多种类型的数据混合运算时系统首先自动将所有数据转换成容量最大的那种数据类型然后再进行计算。 2.当我们把精度(容量)大的数据类型赋值给精度(容量)小的数据类型时,就会报错,反之就会进行自动类型转换。 3.(byte, short)和char之间不会相互自动转换。 4.byte, short, char 他们三者可以计算在计算时首先转化为int类型
public class AutoConvertDetail {//编写一个main方法public static void main(String[] args) {//细节1有多种类型的数据混合运算时//系统首先自动将所有数据转化成容量最大的那种数据类型然后再进行计算。int n1 10; //ok//float d1 n1 1.1; //错误 n1 1.1 结果类型是 doubledouble d2 n1 1.1; //正确 n1 1.1 结果类型是 doublefloat d3 n1 1.1F; //正确 n1 1.1 结果类型是float//细节2当我们把精度容量)大的数据类型赋值给精度容量小的数据类型时//就会报错反之就会自动类型转换。//int n2 1.1; // 错误 double - int//细节3(byte, short) 和 char 之间不会相互自动转换.//当把数赋给 byte 时 先判断该数是否在byte范围内如果是就可以byte b1 10; //正确 -128- 127int n2 1;//byte b2 n2; //错误 原因 如果是变量赋值判段类型////char c1 b1; //错误 原因byte不能自动转化为char//细节4byte, short, char 他们三者可以计算在计算时首先转化为int类型byte b2 1;byte b3 2;short s1 1;//short s2 b2 s1; //错误b2 s1 intint s2 b2 s1; // 正确//byte b4 b2 b3 // 错误 b2 b3 int}} 5.boolean不参与转换 6.自动提升原则表达式结果的类型自动提升为操作数中最大的类型 强制类型转换
介绍
自动类型转换的逆过程将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符( )但可能造成精度降低或溢出,格外要注意。
案例演示
int n1 (int)1.9
System.out.println(n1 n1); //1 造成精度损失 int n2 2000;
byte b1 (byte)n2;
System.out.println(b1 b1); // 造成溢出 基本数据类型和String类型的转换
在程序开发中我们经常需要将基本数据类型转化为String类型或者将String类型转化为基本数据类型。 基本数据类型转String类型
语法将基本类型的值 “ ”即可
案例
int n1 100;
String str1 n1 ; String类型转基本数据类型
语法通过基本类型的包装类调用parseXX方法即可
案例
Integer.parseInt(123);
Double.parseDouble(123.1);
Float.parseFloat(123.45);
String str 123;
int a Integer.parseInt(str); 获取String字符串中的某个字符
语法X.charAt(x) //x代表下标从0开始
String a 123;System.out.println(a.charAt(0)); 六、Java中运算符的使用
1.“”号的使用 1当左右两边都是数值型时则做加法运算
System.out.println(100 98); 结果198 2当左右两边有一方为字符串则做拼接运算
System.out.println100 98); 结果10098
3运算顺序是从左到右
System.out.println100 3 hello); 结果103hello
System.out.printlnhello 100 3); 结果hello1003 2.算术运算符
1i与i在没有表达式的情况下等于i i 1
i 1;
i; //2
i; //2
public class ArithmeticOperation {public static void main(String [] args){//1.面试题1int i 1;i i; //规则使用临时变量1temp i; (2)i i 1; (3)i temp;System.out.println(i); //1//2.面试题2int i 1;i i; //规则使用临时变量(1)i i 1; (2)temp i; (3)i temp;System.out.println(i); //2}
}
3.逻辑运算符
1.ab : 叫逻辑与:规则:当a和b同时为true ,则结果为true否则为false
2. ab : 叫短路与:规则:当a和b同时为true ,则结果为true,否则为false
3.a|b:|叫逻辑或规则:当a和b有一个为true ,则结果为true,否则为false 4.位运算符
1原码、反码、补码重难点
二进制的最高位是符号位:0表示正数,1表示负数(老韩口诀:0-01- -)·正数的原码反码补码都一样(三码合一)负数的反码它的原码符号位不变,其它位取反(0-1,1-0)负数的补码它的反码1负数的反码负数的补码-10的反码补码都是0java没有无符号数换言之, java中的数都是有符号的在计算机运算的时候,都是以补码的方式来运算的.当我们看运算结果的时候要看他的原码(重点)
(2)位运算符
java中有7个位运算(、|、^、~、、和)
√分别是按位与、按位或|、按位异或^,按位取反~它们的运算规则是;
按位与两位一个为0,一个为1结果为1否则为0
按位或 :两位有一个为1结果为1否则为0
按位异或:两位一个为0,一个为1结果为1否则为0
按位取反~:0-1 ,1-0
比如:23? ~-2 ~2 ?
public class BitOp {//编写一个main方法public static void main(String [] args) {//计算 2 3//推导过程//1. 先得到 2 的补码 2的原码 00000000 00000000 00000000 00000010// 2 的补码 00000000 00000000 00000000 00000010 正数的原码、补码、反码一样//2. 3 的原码 00000000 00000000 00000000 00000011// 3 的补码 00000000 00000000 00000000 00000011//3. 按位// 00000000 00000000 00000000 00000010// 00000000 00000000 00000000 00000011// 00000000 00000000 00000000 00000010 运算后的补码//运算后的原码是 00000000 00000000 00000000 00000010 正数的原码、补码、反码一样System.out.println(23); //2//推导过程//1. 先得到 -2 的反码 -2的原码 10000000 00000000 00000000 00000010//2. -2 的反码 11111111 11111111 11111111 11111101//3. -2 的补码 11111111 11111111 11111111 11111110//4. ~-2 的操作 00000000 00000000 00000000 00000001 运算后的补码//5.运算后的原码 00000000 00000000 00000000 00000001 正数的原码、补码、反码一样System.out.println(~-2); //1//推导过程//1. 先得到 2 的补码 2的原码 00000000 00000000 00000000 00000010//2. 2 的补码 00000000 00000000 00000000 00000010//3. ~2操作 11111111 11111111 11111111 11111101 运算后的补码 注意是负数//4. 2的反码 11111111 11111111 11111111 11111100//5. 2的原码 10000000 00000000 00000000 00000011//6. 运算后的结果 -3System.out.println(~2); //-3}
}
●还有3个位运算符、和运算规则:
算术右移:低位溢出,符号位不变,并用符号位补溢出的高位算术左移符号位不变,低位补0逻辑右移也叫无符号右移,运算规则是:低位溢出高位补О特别说明:没有符号
举例
int a 1 2; //1 00000001 00000000 本质 1/ 2/ 2 0
int c 1 2; //1 00000001 00000100 本质 1*2*2 4
public class BitOperation {public static void main(String [] args) {System.out.println(1 2); // 0System.out.println(1 2); //4System.out.println(4 3); // 4 * 2 * 2 * 2 32System.out.println(4 3); // 15 / 2/ 2 3}
}
七、键盘输入语句
介绍
在编程中需要接受用输入的数据就可以使用键盘输入语句来获取。需要一个扫描器对象就是Scanner。 import java.util.Scanner; //表示把java.util下的Scanner类导入
public class Input {//编写一个main方法public static void main(String [] args) {//Scanner类 表示 简单文本扫描器 在java.util包//1.引入/导入 Scanner类所在的包//2.创建 Scanner 对象 new 创建一个对象//myscanner 就是Scanner类的对象//Scanner myscanner new Scanner(System.in);System.out.println(请输入名字);String name myscanner.next(); //接收用户输入字符串System.out.println(请输入年龄);int age myscanner.nextInt(); //接收用户输入年龄System.out.println(请输入薪水);double salary myscanner.nextDouble(); //接收用户输入薪水System.out.println(人的信息如下);System.out.println(名字: name \t 年龄 age \t 薪水: salary );}
}
八、控制结构 和C语言差不多注意要创建Scanner对象。
public class NineTable {public static void main(String [] args) {for(int i 1; i 9; i) {for(int j 1; j i; j) {System.out.print(i * j i * j \t);}System.out.println();}}
}
代码需要注意print和println的输出格式 print为一般输出不能换行输出 println为换行输出。
public class Stars {public static void main(String [] args) {/* 1.打印整个金字塔** // 第1层 有 1个 * 2 * 1 -1 有4 总层数-1个空格* * * // 第2层 有 3个 * 2 * 2 -1 有3 总层数-2个空格* * * * * // 第3层 有 5个 * 2 * 3 -1 有2 总层数-3个空格* * * * * * * // 第4层 有 7个* 2 * 4 -1 有1 总层数-4个空格
* * * * * * * * * // 第5层 有 9个* 2 * 5 -1 有0 总层数-5个空格4.打印空心的金字塔* // 第1层 有 1个 ** * // 第2层 有 2个 ** * // 第3层 有 2个 ** * // 第4层 有 2个 ** * * * * * * * * // 第5层 有 9个 **/for(int i 1; i 5; i) { //i 表示层数//在输出*之前 还有输出 对应空格 总层数-当前层for(int k 1; k 5 - i; k) {System.out.print( );}//控制打印每层的*个数for(int j 1; j 2 * i -1 ; j) {System.out.print(*);}//每打印完一层的*后就换行 println本身会换行System.out.println();}for(int i 1; i 5; i) { //i 表示层数//在输出*之前 还有输出 对应空格 总层数-当前层for(int k 1; k 5 - i; k) {System.out.print( );}//控制打印每层的*个数for(int j 1; j 2 * i -1 ; j) {//当前行的第一个位置是*最后一个位置也是*最后一层全部 *if(j 1 || j 2 * i -1 || i 5) {System.out.print(*);} else {System.out.print( );}}//每打印完一层的*后就换行 println本身会换行System.out.println();}}
}
九、数组
1.动态初始化
数组的定义
(1)
语法数据类型 数组名[] new 数据类型[大小]
int a[] new int[5];//创建一个数组名字a存放5个int。
(2)
语法数据类型 数组名[]//先声明这时候还没有分配空间。
数组名 new 数据类型[大小]//分配空间。
2.静态初始化
语法数据类型 数组名[] {元素值元素值...}
int a[] {2, 4, 5, 6, 7, 8, 9, 23, 54};如果知道数组有多少元素具体值
相当于动态初始化。 3.数组赋值
1引用赋值
数组在默认情况下是引用传递赋的值是地址 赋值方式是引用赋值是一个地址。
public class ArrayAssign {public static void main(String[] args) {int arr1[] {1,2,3};int arr2[] arr1; //把 arr1赋给 arr2arr2[0] 10;//看看arr1的值for(int i 0; i 3; i) {System.out.println(arr1[i]); 此时输出结果是 10 、2 、3}}
} 通过上面代码块和图解可以证明数组赋值方式是引用赋值。
2拷贝赋值
public class ArrayAssign {public static void main(String[] args) {int arr1[] {10,20,30};//创建一个新的数组arr2开辟新的数据空间大小 arr1.lengthint arr2[] new int[arr1.length];int arr2[] arr1; //把 arr1赋给 arr2arr2[0] 100;//看看arr1的值for(int i 0; i 3; i) {System.out.println(arr1[i]); 此时输出结果是 10 、20 、30}//看看arr2的值for(int i 0; i 3; i) {System.out.println(arr2[i]); 此时输出结果是 100、20 、30}}
} 由以上代码和图解可见拷贝赋值的数组会开辟一个新的空间两者之间互不影响。 4.多维数组-二维数组
静态二维数组
语法数据类型 数组名[] new 数据类型[大小]
int a[][] new int[2][3];//创建一个二维数组名字a存放2 * 3个int。 动态创建二维数组
public class TwoDimensionalArray {public static void main(String [] args) {/*看一个需求 动态创建下面的二维数组并输出i 0: 1i 1: 2 2i 2: 3 3 3一个有三个一维数组每个一维数组的元素是不一样的。*/int arr[][] new int[3][]; //创建一个二维数组 但是只是确定一维数组的个数for(int i 0; i arr.length; i) { //遍历arr每个一维数组//给每个一维数组开空间 new//如果没有给一维数组 new 那么 arr[i] 就是nullarr[i] new int[i 1];//遍历一维数组 并给一维数组的每个元素复制for(int j 0; j arr[i].length; j) {arr[i][j] i 1; //赋值}}System.out.print(arr元素);System.out.println();for(int i 0; i arr.length; i) {for(int j 0; j arr[i].length; j)System.out.print(arr[i][j] );System.out.println();}}
}
杨辉三角
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
......
规律
第一行有 1 个元素第 n 行有 n 个元素。每一行的第一个元素和最后一个元素都是1.从第三行开始对于非第一个元素和最后一个元素的元素的值arr[i][j] arr[i-1][j] arr[i-1][j-1]
public class YangHui {//编写一个main方法public static void main(String [] args) {int yangHui[][] new int[10][];for(int i 0; i yangHui.length; i) {//给每个一维数组(行)开辟空间yangHui[i] new int[i 1];//给每个一维数组(行)赋值for(int j 0; j yangHui[i].length; j) {//每一行的第一个元素和最后一个元素都是1if(j 0 || j yangHui[i].length-1 ) {yangHui[i][j] 1;} else { //中间的元素yangHui[i][j] yangHui[i-1][j] yangHui[i-1][j-1];}}}//遍历杨辉三角for(int i 0; i yangHui.length; i) {for (int j 0; j yangHui[i].length; j) {System.out.print(yangHui[i][j] );}System.out.println();}}
} 程序运行结果 十、排序
冒泡排序
public class BubbleSort {public static void main(String [] args) {int arr[] {24, 69, 80, 57, 13};int temp 0;for(int i 0 ; i 5; i ) {for(int j 0; j 5-i-1; j) {if(arr[j] arr[j 1]) {temp arr[j];arr[j] arr[j 1];arr[j 1] temp;}}}for(int k 0 ; k 5; k) {System.out.print(arr[k] ); // 13 24 57 69 80 }}
}
运用包里面的排序函数
import java.util.Arrays;
public class BubbleSort {public static void main(String [] args) {int arr[] {24, 69, 80, 57, 13};Arrays.sort(arr);for(int k 0 ; k 5; k) {System.out.print(arr[k] ); // 13 24 57 69 80 }}
}
十一、面向对象之类与对象
特征封装、继承、多态。
1、面向对象简述
面向对象是一种现在最为流行的程序设计方法几乎现在的所有应用都以面向对象为主了最早的面向对象的概念实际上是由IBM提出的在70年代的Smaltalk语言之中进行了应用后来根据面向对象的设计思路才形成C而由C产生了Java这门面向对象的编程语言。 但是在面向对象设计之前广泛采用的是面向过程面向过程只是针对于自己来解决问题。面向过程的操作是以程序的基本功能实现为主实现之后就完成了也不考虑修改的可能性面向对象更多的是要进行子模块化的设计每一个模块都需要单独存在并且可以被重复利用所以面向对象的开发更像是一个具备标准的开发模式。 在面向对象定义之中也规定了一些基本的特征
1封装保护内部的操作不被破坏
2继承在原本的基础之上继续进行扩充
3多态在一个指定的范围之内进行概念的转换。 对于面向对象的开发来讲也分为三个过程OOA面向对象分析、OOD面向对象设计、OOP面向对象编程。
2、类与对象的基本概念
类与对象时整个面向对象中最基础的组成单元。 类是抽象的概念集合表示的是一个共性的产物类之中定义的是属性和行为方法
对象对象是一种个性的表示表示一个独立的个体每个对象拥有自己独立的属性依靠属性来区分不同对象。 可以一句话来总结出类和对象的区别类是对象的模板对象是类的实例。类只有通过对象才可以使用而在开发之中应该先产生类之后再产生对象。类不能直接使用对象是可以直接使用的。
3、类与对象的定义和使用
在Java中定义类使用关键字class完成。语法如下
class 类名称 {属性 (变量) ;行为 (方法) ;
}
范例定义一个Person类
class Person { // 类名称首字母大写String name ;int age ;public void tell() { // 没有staticSystem.out.println(姓名 name 年龄 age) ;}
}
类定义完成之后肯定无法直接使用。如果要使用必须依靠对象那么由于类属于引用数据类型所以对象的产生格式两种格式如下
1格式一声明并实例化对象
类名称 对象名称 new 类名称 () ;
2格式二先声明对象然后实例化对象
类名称 对象名称 null ;
对象名称 new 类名称 () ; 引用数据类型与基本数据类型最大的不同在于引用数据类型需要内存的分配和使用。所以关键字new的主要功能就是分配内存空间也就是说只要使用引用数据类型就要使用关键字new来分配内存空间。 当一个实例化对象产生之后可以按照如下的方式进行类的操作
对象.属性表示调用类之中的属性
对象.方法()表示调用类之中的方法。 范例使用对象操作类
package com.wz.classandobj;class Person { String name ;int age ;public void get() {System.out.println(姓名 name 年龄 age);}
}public class TestDemo {public static void main(String args[]) {Person per new Person() ;// 声明并实例化对象per.name 张三 ;//操作属性内容per.age 30 ;//操作属性内容per.get() ;//调用类中的get()方法}
}
运行结果
姓名张三年龄30
以上完成了一个类和对象的操作关系下面换另外一个操作来观察一下
package com.wz.classandobj;class Person { String name ;int age ;public void get() {System.out.println(姓名 name 年龄 age);}
}public class TestDemo {public static void main(String args[]) {Person per null;//声明对象per new Person() ;//实例化对象per.name 张三 ;//操作属性内容per.age 30 ;//操作属性内容per.get() ;//调用类中的get()方法}
}
运行结果
姓名张三年龄30
那么问题来了以上两种不同的实例化方式有什么区别呢
我们从内存的角度分析。首先给出两种内存空间的概念
1堆内存保存对象的属性内容。堆内存需要用new关键字来分配空间
2栈内存保存的是堆内存的地址在这里为了分析方便可以简单理解为栈内存保存的是对象的名字。 任何情况下只要看见关键字new都表示要分配新的堆内存空间一旦堆内存空间分配了里面就会有类中定义的属性并且属性内容都是其对应数据类型的默认值。
于是上面两种对象实例化对象方式内存表示如下 两种方式的区别在于①②第一种声明并实例化的方式实际就是①②组合在一起而第二种先声明然后实例化是把①和②分步骤来。
另外如果使用了没有实例化的对象结果如何 如下 运行结果
Exception in thread main java.lang.NullPointerException
at com.wz.classandobj.TestDemo.main(TestDemo.java:15)
此时程序只声明了Person对象但并没有实例化Person对象只有了栈内存并没有对应的堆内存空间则程序在编译的时候不会出现任何的错误但是在执行的时候出现了上面的错误信息。这个错误信息表示的是“NullPointerException空指向异常”这种异常只要是应用数据类型都有可能出现。
4、对象引用传递初步分析
引用传递的精髓同一块堆内存空间可以同时被多个栈内存所指向不同的栈可以修改同一块堆内存的内容。
下面通过若干个程序以及程序的内存分配图来进行代码的讲解。
我们来看一个范例
class Person { String name ;int age ;public void tell() { System.out.println(姓名 name 年龄 age) ;}
}
public class TestDemo {public static void main(String args[]) {Person per1 new Person() ; // 声明并实例化对象per1.name 张三 ;per1.age 20 ;Person per2 per1 ; // 引用传递per2.name 李四 ;per1.tell() ;}
}
对应的内存分配图如下 再来看另外一个
class Person {String name ;int age ;public void tell() {System.out.println(姓名 name 年龄 age) ;}
}
public class TestDemo {public static void main(String args[]) {Person per1 new Person() ; // 声明并实例化对象Person per2 new Person() ;per1.name 张三 ;per1.age 20 ;per2.name 李四 ;per2.age 30 ;per2 per1 ;// 引用传递per2.name 王五 ;per1.tell() ;}
} 对应的内存分配图如下 垃圾指的是在程序开发之中没有任何对象所指向的一块堆内存空间这块空间就成为垃圾所有的垃圾将等待GC垃圾收集器不定期的进行回收与空间的释放。
5、方法的重载
Java中的方法重载在一个类中可以存在多个方法名相同的方法但是参数列表不同。同一个类中定义的多个方法之间的关系满足下列的多个方法互相构成重载
1.多个方法在同一个类中。2.多个方法具有相同的方法名。3.多个方法的参数不相同类型不同或者数量不同。、
public class text1 {public static void main(String[] args) {show(10);show(10,11);}public static void show(int a){System.out.println(a);}public static void show(int a ,int b){System.out.println(a b);}
}
结果
10
10 11
public class text1 {public static void main(String[] args) {show(10);show(a);}public static void show(int a){System.out.println(a);}public static void show(char a){System.out.println(a);}
}
结果
10
a
public class text1 {public static void main(String[] args) {show(a,10);show(10,a);}public static void show(int b,char a){System.out.println(b a);}public static void show(char a,int b){System.out.println(a b);}
}
结果
a 10
10 a
6、可变参数
基本概念
java允许将同一个类中多个同名同功能但参数个数不同的方法封装成一个方法。
基本语法
访问修饰符 返回类型 方法名(数据类型... 形参名) {}
public class VarParameter {public static void main(String [] args) {HspMethod Sum new HspMethod();System.out.println(Sum.sum(1,2,3));}
}class HspMethod {public int sum(int... nums) {//System.out.println(接受的参数个数 nums.length);int res 0;for(int i 0; i nums.length; i) {res nums[i];}return res;}
}注意事项和细节
(1)可变参数可以是0个或者任意个。
(2)可变参数的实参可以是数组。
(3)可变参数的本质就是数组。
(4)可变参数可以和普通类型的参数一起放在形参列表但必须保证可变参数在最后。
(5)一个形参列表中只能出现一个可变参数。
public class VarParameter {public static void main(String [] args) {HspMethod Sum new HspMethod();System.out.println(Sum.showScore(小明,90,100));}
}class HspMethod {public String showScore(String name, double... score) {double totalScore 0;for(int i 0; i score.length; i) {totalScore score[i];}return name 的总成绩为 totalScore;}
}
//小明的总成绩为190.0
7、构造器
需求前面我们在创建人类的对象时候是先把一个对象创建好后再给他的年龄和姓名属性赋值如果我们现在要求在创建人类的对象时就直接指定这个对象的年龄和姓名该怎么做这时就可以使用构造器。
基本语法
[修饰符] 方法名(形参列表) {
方法体;
}
说明
(1)构造起的修饰符可以默认
(2)构造器没有返回值
(3)方法名和类名字必须一样
(4)参数列表和成员方法一样的规则
(5)构造器的调用系统完成
public class Constructor01 {public static void main(String[] args) {Person01 p1 new Person01(smith, 80);System.out.println(p1的信息如下);System.out.println(p1对象name p1.name);System.out.println(p1对象age p1.age);}
}class Person01 {String name;int age;//构造器//1. 构造器没有返回值也不能写void//2. 构造器的名称和类Person一样//3. (String pName, int pAge) 是构造器形参列表 规则和成员方法一样。public Person01(String pName, int pAge) {System.out.println(构造器被调用~ 自动完成对象的属性初始化);name pName;age pAge;}
}
//结果
//构造器被调用~ 自动完成对象的属性初始化
//p1的信息如下
//p1对象name smith
//p1对象age 80
注意事项和细节要点
一个类可以定义多个不同的构造器即构造器重载。比如我们可以再给Person类定义一个构造器用来创建对象的时候只指定人名字不需要指定年龄。构造器名和类名要相同构造器没有返回值构造器是完成对象的初始化并不是创建对象。在创建对象时系统自动的调用该类的构造方法如果程序员没有定义构造器系统会自动给类生成一个默认无参构造器(也叫默认构造器),比如
Person(){},使用javap指令 反编译看看
7. 一旦定义了自己的构造器默认的构造器就覆盖了就不能再使用默认的无参构造器除非显示的定义一下即:Person(){} 8、this关键字
this的注意事项和使用细节
this关键字可以用来访问本类的属性、方法、构造器。this用于区分当前类的属性和局部变量。访问成员方法的语法this.方法名(参数列表);访问构造器语法this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器必须放在第一条语句)。this不能在类定义的外部使用只能在类定义的方法中使用。
public class ThisDetail {public static void main(String[] args) {T1 t1 new T1();t1.f2();t1.f3();}
}class T1 {String name 小明;int num 10;public T1() {//this(jack, 100);System.out.println(T() 构造器);}public T1(String name, int age) {System.out.println(T(String name, int age) 构造器);}//this关键字可以用来访问本类的属性public void f3() {String name smith;//传统方式System.out.println(name name num num); //name smith num 10//也可以使用this访问属性System.out.println(name this.name num this.num); // name 小明 num 10}//细节 访问成员方法的语法 this.方法名(参数列表);public void f1() {System.out.println(f1() 方法...);}public void f2() {System.out.println(f2() 方法...);//调用本类中的f1//第一种方式f1();//第二种方式this.f1();}
}
//结果
//T(String name, int age) 构造器
//T() 构造器
//f2() 方法...
//f1() 方法...
//f1() 方法...
//name smith num 10
//name 小明 num 10
9、访问修饰符
4种访问修饰符的访问范围 10、封装
封装介绍
封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
package com.hspedu.encap;public class Encapsulation {public static void main(String[] args) {Person person new Person();person.setName(jack);person.setAge(30);person.setSalary(30000);System.out.println(person.info());System.out.println(person.getSalary());//如果我们自己使用构造器指定属性Person smith new Person(smith, 2000, 50000);System.out.println(smith的信息);System.out.println(smith.info());}}
/*
那么在java中如何实现这种类似的控制呢
请大家看一个小程序(com.hspedu.encap: Encapsulation01.java)
石能随便查看人的年龄工资等隐私并对设置的年龄进行合理的验证。年龄合理就设置否则给默认年龄
必须在1-12味年龄工资不能直接查看name的长度在2-6字符之间
*/class Person {public String name;private int age;private double salary;//构造器 alt insertpublic Person(String name, int age, double salary) {
// this.name name;
// this.age age;
// this.salary salary;this.setName(name);this.setAge(age);this.setSalary(salary);}public Person() {}public String getName() {return name;}public void setName(String name) {//加入对数据的校验,南当于增加了业务逻辑if (name.length() 2 name.length() 6) {this.name name;} else {System.out.println(name的长度不在2-6字符之间,给默认名字);this.name json;}}public int getAge() {return age;}public void setAge(int age) {if (age 1 age 120) {this.age age;} else {System.out.println(年龄需要在1-120);this.age 18; //给一个默认年龄}}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary salary;}public String info() {return 信息为 name name age age 薪水 salary;}}好处
1只能通过规定的方法访问数据 2隐藏类的实例细节方便修改和实现。
11、继承
继承基本介绍和示意图
继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法所有的子类不需要重新定义这些属性和方法只需要通过extends来声明继承父类即可。
示意图 基本语法
class 子类 extends 父类 {
}
1)子类就会自动拥有父类定义的属性和方法。
2)父类又叫超类基类。
3)子类又叫派生类。
12、supper关键字
基本介绍
super 代表父类的引用用于访问父类的属性、方法、构造器
基本语法 package com.hspedu.super_;
public class A extends Base{//4 个属性 //public int n1 100; protected int n2 200; int n3 300;private int n4 400; public A() {} public A(String name) {} public A(String name, int age) {} // public void cal() { // System.out.println(A 类的 cal() 方法...); // }public void test100() {}protected void test200() { }void test300() { }private void test400() { }
}
package com.hspedu.super_;
public class B extends A { public int n1 888; //编写测试方法 public void test() { //super 的访问不限于直接父类如果爷爷类和本类中有同名的成员也可以使用 super 去访问爷爷类的成员 // 如果多个基类(上级类)中都有同名的成员使用 super 访问遵循就近原则。A-B-C System.out.println(super.n1 super.n1); super.cal(); }//访问父类的属性 , 但不能访问父类的 private 属性 [案例]super.属性名 public void hi() { System.out.println(super.n1 super.n2 super.n3 ); }public void cal() { System.out.println(B 类的 cal() 方法...); }public void sum() { System.out.println(B 类的 sum()); //希望调用父类-A 的 cal 方法 //这时因为子类 B 没有 cal 方法因此我可以使用下面三种方式 //找 cal 方法时(cal() 和 this.cal())顺序是: // (1)先找本类如果有则调用 // (2)如果没有则找父类(如果有并可以调用则调用) // (3)如果父类没有则继续找父类的父类,整个规则就是一样的,直到 Object 类 // 提示如果查找方法的过程中找到了但是不能访问 则报错, cannot access // 如果查找方法的过程中没有找到则提示方法不存在 //cal(); this.cal(); //等价 cal //找 cal 方法(super.call()) 的顺序是直接查找父类其他的规则一样 //super.cal(); //演示访问属性的规则//n1 和 this.n1 查找的规则是 //(1) 先找本类如果有则调用 //(2) 如果没有则找父类(如果有并可以调用则调用) //(3) 如果父类没有则继续找父类的父类,整个规则就是一样的,直到 Object 类 // 提示如果查找属性的过程中找到了但是不能访问 则报错, cannot access // 如果查找属性的过程中没有找到则提示属性不存在 System.out.println(n1); System.out.println(this.n1); //找 n1 (super.n1) 的顺序是直接查找父类属性其他的规则一样 System.out.println(super.n1); }//访问父类的方法不能访问父类的 private 方法 super.方法名(参数列表); public void ok() { super.test100(); super.test200(); super.test300(); //super.test400();//不能访问父类 private 方法 }//访问父类的构造器(这点前面用过)super(参数列表);只能放在构造器的第一句只能出现一句 public B() { //super(); //super(jack, 10); super(jack); }
}
package com.hspedu.super_;
public class Super01 { public static void main(String[] args) { B b new B();//子类对象 //b.sum(); b.test(); }
}
package com.hspedu.super_;
public class Base { //父类是 Object public int n1 999; public int age 111; public void cal() { System.out.println(Base 类的 cal() 方法...); }public void eat() { System.out.println(Base 类的 eat().....); }
}
13、多态
介绍面向对象的最后一个特性就是多态那么什么是多态呢 多态就是对象的多种形态。
java里面的多态主要变现在两个方面
13.1 引用多态
父类的引用可以指向本类的对象
父类的引用可以指向子类的对象
这两句话是什么意思呢让我们用代码来体验一下首先我们创建一个父类Animal和一个子类Dog在主函数里如下所示 注意我们不能使用一个子类的引用来指向父类的对象如 。
这里我们必须深刻理解引用多态的意义才能更好记忆这种多态的特性。为什么子类的引用不能用来指向父类的对象呢我在这里通俗给大家讲解一下就以上面的例子来说我们能说“狗是一种动物”但是不能说“动物是一种狗”狗和动物是父类和子类的继承关系它们的从属是不能颠倒的。当父类的引用指向子类的对象时该对象将只是看成一种特殊的父类里面有重写的方法和属性反之一个子类的引用来指向父类的对象是不可行的
13.2 方法多态
根据上述创建的两个对象本类对象和子类对象同样都是父类的引用当我们指向不同的对象时它们调用的方法也是多态的。
创建本类对象时调用的方法为本类方法
创建子类对象时调用的方法为子类重写的方法或者继承的方法
使用多态的时候要注意如果我们在子类中编写一个独有的方法没有继承父类的方法此时就不能通过父类的引用创建的子类对象来调用该方法
注意 继承是多态的基础。
十二、引用类型转换
了解了多态的含义后我们在日常使用多态的特性时经常需要进行引用类型转换。
引用类型转换
1. 向上类型转换(隐式/自动类型转换)是小类型转换到大类型。
就以上述的父类Animal和一个子类Dog来说明当父类的引用可以指向子类的对象时就是向上类型转换。如 2. 向下类型转换(强制类型转换)是大类型转换到小类型(有风险,可能出现数据溢出)。
将上述代码再加上一行我们再次将父类转换为子类引用那么会出现错误编译器不允许我们直接这么做虽然我们知道这个父类引用指向的就是子类对象但是编译器认为这种转换是存在风险的。如 那么我们该怎么解决这个问题呢我们可以在animal前加上Dog来强制类型转换。如 但是如果父类引用没有指向该子类的对象则不能向下类型转换虽然编译器不会报错但是运行的时候程序会出错如 其实这就是上面所说的子类的引用指向父类的对象而强制转换类型也不能转换
还有一种情况是父类的引用指向其他子类的对象则不能通过强制转为该子类的对象。如 这是因为我们在编译的时候进行了强制类型转换编译时的类型是我们强制转换的类型所以编译器不会报错而当我们运行的时候程序给animal开辟的是Dog类型的内存空间这与Cat类型内存空间不匹配所以无法正常转换。这两种情况出错的本质是一样的所以我们在使用强制类型转换的时候要特别注意这两种错误下面有个更安全的方式来实现向下类型转换。。。。
3. instanceof运算符来解决引用对象的类型避免类型转换的安全性问题。
instanceof是Java的一个二元操作符和是同一类东东。由于它是由字母组成的所以也是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例返回boolean类型的数据。
我们来使用instanceof运算符来规避上面的错误代码修改如下 利用if语句和instanceof运算符来判断两个对象的类型是否一致。
补充说明在比较一个对象是否和另一个对象属于同一个类实例的时候我们通常可以采用instanceof和getClass两种方法通过两者是否相等来判断但是两者在判断上面是有差别的。Instanceof进行类型检查规则是:你属于该类吗或者你属于该类的派生类吗而通过getClass获得类型信息采用来进行检查是否相等的操作是严格的判断,不会存在继承方面的考虑
总结在写程序的时候如果要进行类型转换我们最好使用instanceof运算符来判断它左边的对象是否是它右边的类的实例再进行强制转换。
十三、抽象类
定义抽象类前使用abstract关键字修饰则该类为抽象类。
使用抽象类要注意以下几点
1. 抽象类是约束子类必须有什么方法而并不关注子类如何实现这些方法。
2. 抽象类应用场景
a. 在某些情况下某个父类只是知道其子类应该包含怎样的方法但无法准确知道这些子类如何实现这些方法(可实现动态多态)。
b. 从多个具有相同特征的类中抽象出一个抽象类以这个抽象类作为子类的模板从而避免子类设计的随意性。
3. 抽象类定义抽象方法只有声明不需要实现。抽象方法没有方法体以分号结束抽象方法必须用abstract关键字来修饰。如: 4、包含抽象方法的类是抽象类。抽象类中可以包含普通的方法也可以没有抽象方法。如 5、抽象类不能直接创建可以定义引用变量来指向子类对象来实现抽象方法。以上述的Telephone抽象类为例
public abstract class Telephone {public abstract void call();//抽象方法方法体以分号结束只有声明不需要实现public void message(){System.out.println(我是抽象类的普通方法);}//抽象类中包含普通的方法
}
public class Phone extends Telephone {public void call() {//继承抽象类的子类必须重写抽象方法// TODO Auto-generated method stubSystem.out.println(我重写了抽象类的方法);}}
以上是Telephone抽象类和子类Phone的定义下面我们看main函数里 运行结果排错之后 十四、接口
1、概念
接口可以理解为一种特殊的类由全局常量和公共的抽象方法所组成。也可理解为一个特殊的抽象类因为它含有抽象方法。
如果说类是一种具体实现体而接口定义了某一批类所需要遵守的规范接口不关心这些类的内部数据也不关心这些类里方法的实现细节它只规定这些类里必须提供的某些方法。这里与抽象类相似
2.接口定义的基本语法
[修饰符] [abstract] interface 接口名 [extends父接口1,2....]多继承{
0…n常量 (public static final)
0…n 抽象方法(public abstract)
}
其中[ ]里的内容表示可选项可以写也可以不写;接口中的属性都是常量即使定义时不添加public static final 修饰符系统也会自动加上接口中的方法都是抽象方法即使定义时不添加public abstract修饰符系统也会自动加上。
3.使用接口
一个类可以实现一个或多个接口实现接口使用implements关键字。java中一个类只能继承一个父类是不够灵活的通过实现多个接口可以补充。
继承父类实现接口的语法为
[修饰符] class 类名 extends 父类 implements 接口1接口2...{
类体部分//如果继承了抽象类需要实现继承的抽象方法要实现接口中的抽象方法
}
注意如果要继承父类继承父类必须在实现接口之前,即extends关键字必须在implements关键字前
补充说明通常我们在命名一个接口时经常以I开头用来区分普通的类。如IPlayGame
以下我们来补充在上述抽象类中的例子我们之前已经定义了一个抽象类Telephone和子类Phone这里我们再创建一个IPlayGame的接口然后在原来定义的两个类稍作修改代码如下
public interface IPlayGame {public void paly();//abstract 关键字可以省略系统会自动加上public String name游戏名字;//static final关键字可以省略系统会自动加上
}
public class Phone extends Telephone implements IPlayGame{public void call() {//继承抽象类的子类必须重写抽象方法// TODO Auto-generated method stubSystem.out.println(我重写了抽象类的方法);}Overridepublic void paly() {// TODO Auto-generated method stubSystem.out.println(我重写了接口的方法);}}public class train {public static void main(String[] args) {// TODO Auto-generated method stubIPlayGame inew Phone();//用接口的引用指向子类的对象i.paly();//调用接口的方法System.out.println(i.name);//输出接口的常量}
}
十五、抽象类和接口的区别
我们在多态的学习过程中认识到抽象类和接口都是实现java多态特性的关键部分两者都包含抽象方法只关注方法的声明而不关注方法的具体实现那么这两者又有什么区别呢我们在编写java程序的时候又该如何抉择呢
参考博文深入理解Java的接口和抽象类 - 大数据从业者FelixZh - 博客园
1语法层面上的区别
1.一个类只能继承一个抽象类而一个类却可以实现多个接口。 2.抽象类中的成员变量可以是各种类型的而接口中的成员变量只能是public static final类型的且必须给其初值所以实现类中不能重新定义也不能改变其值抽象类中的变量默认是 friendly 型其值可以在子类中重新定义也可以重新赋值。 3.抽象类中可以有非抽象方法接口中则不能有非抽象方法。 4.接口可以省略abstract 关键字抽象类不能。 5.接口中不能含有静态代码块以及静态方法而抽象类可以有静态代码块和静态方法
2设计层面上的区别
1抽象类是对一种事物的抽象即对类抽象而接口是对行为的抽象。抽象类是对整个类整体进行抽象包括属性、行为但是接口却是对类局部行为进行抽象。举个简单的例子飞机和鸟是不同类的事物但是它们都有一个共性就是都会飞。那么在设计的时候可以将飞机设计为一个类Airplane将鸟设计为一个类Bird但是不能将 飞行 这个特性也设计为类因此它只是一个行为特性并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly包含方法fly( )然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机比如战斗机、民用飞机等直接继承Airplane即可对于鸟也是类似的不同种类的鸟直接继承Bird类即可。从这里可以看出继承是一个 是不是的关系而 接口 实现则是 有没有的关系。如果一个类继承了某个抽象类则子类必定是抽象类的种类而接口实现则是有没有、具备不具备的关系比如鸟是否能飞或者是否具备飞行这个特点能飞行则可以实现这个接口不能飞行就不实现这个接口。
2设计层面不同抽象类作为很多子类的父类它是一种模板式设计。而接口是一种行为规范它是一种辐射式设计。什么是模板式设计最简单例子大家都用过ppt里面的模板如果用模板A设计了ppt B和ppt Cppt B和ppt C公共的部分就是模板A了如果它们的公共部分需要改动则只需要改动模板A就可以了不需要重新对ppt B和ppt C进行改动。而辐射式设计比如某个电梯都装了某种报警器一旦要更新报警器就必须全部更新。也就是说对于抽象类如果需要添加新的方法可以直接在抽象类中添加具体的实现子类可以不进行变更而对于接口则不行如果接口进行了变更则所有实现这个接口的类都必须进行相应的改动。
下面看一个网上流传最广泛的例子门和警报的例子门都有open( )和close( )两个动作此时我们可以定义通过抽象类和接口来定义这个抽象概念
abstract class Door {public abstract void open();public abstract void close();
}
//或者
interface Door {public abstract void open();public abstract void close();
}
但是现在如果我们需要门具有报警alarm( )的功能那么该如何实现下面提供两种思路
1将这三个功能都放在抽象类里面但是这样一来所有继承于这个抽象类的子类都具备了报警功能但是有的门并不一定具备报警功能
2将这三个功能都放在接口里面需要用到报警功能的类就需要实现这个接口中的open( )和close( )也许这个类根本就不具备open( )和close( )这两个功能比如火灾报警器。
从这里可以看出 Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为open()和close()属于门本身固有的行为特性而alarm()属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口包含alarm()行为,Door设计为单独的一个抽象类包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。
interface Alram {void alarm();
}abstract class Door {void open();void close();
}class AlarmDoor extends Door implements Alarm {void oepn() {//....}void close() {//....}void alarm() {//....}
}
十六、递归
一、例题
1、斐波那契数列
public class Fibonacci {public static void main(String [] args) {F f new F();int bf4 f.fibonacci(7);System.out.println(当n 7时 对应的斐波那契数 bf4);}
}class F {public int fibonacci(int n) {if(n 1) {if (n 1 || n 2) {return 1;} else {return fibonacci(n - 1) fibonacci(n - 2);}} else {System.out.println(请输入 1的数!);return -1;}}
}2、猴子吃桃子
猴子吃桃子问题有一堆桃子猴子第一个天吃了其中的一半并在多吃一个
以后每天猴子都吃其中的一半然后再多吃一个。当到第10天时想再吃时
发现只有1个桃子。 问题最初共多少个桃子
public class Fibonacci {public static void main(String [] args) {F f new F();int bf4 f.peach(1);System.out.println(桃子的最初总数为 bf4);}
}class F {//思路分析 逆推//1. day 10 时 有1个桃子//2. day 9 时 有(day10 1) * 2 4//3. day 8 时 有(day9 1) * 2 10//4. 规律就是 前一天的桃子 (后一天的桃子 1) *2//5. 递归public int peach(int day) {if (day 10) {return 1;} else if (day 1 day 9){return (peach(day 1) 1) * 2;} else {System.out.println(day在1-10);return -1;}}
}
//共有1534个桃子 3、迷宫问题
public class Labyrinth {public static void main(String [] args) {//初始化数组int[][] map new int[8][7];//将第一行和最后一行设为1for(int i 0; i 7; i) {map[0][i] 1;map[7][i] 1;}//将第一列和最后一列设为1for(int i 0; i 8; i) {map[i][0] 1;map[i][6] 1;}//将第四行第二三列设为1map[3][1] 1;map[3][2] 1;//输出当前地图System.out.println(当前的地区如下所示);for (int i 0; i map.length; i) {for (int j 0; j map[i].length; j) {System.out.print(map[i][j] );}System.out.println();}//使用findWay给老鼠找路T t1 new T();t1.findWay(map, 1, 1);System.out.println(找路的情况如下所示);for (int i 0; i map.length; i) {for (int j 0; j map[i].length; j) {System.out.print(map[i][j] );}System.out.println();}}
}class T {//使用递归回溯的思想来解决老鼠出迷宫//思路解读//1. findWay方法就是专门来找出迷宫的路径//2. 如果找到就返回true否则返回false//3.map 就是二维数组即表示迷宫//4. ij就是老鼠的位置初始化的位置为11//5. 因为我们是递归的找路所以我先规定 map数组的各个值的含义// 0 表示可以走 1 表示障碍物 2 表示可以走 3 表示走过但是走不通是思路//6. 当map[6][5] 2 就说明找到通路就可以结束 否则就继续找//7. 先确定老鼠找路策略 下-右-上-左public boolean findWay(int[][] map, int i, int j) {if (map[6][5] 2) {//说明已经找到return true;} else {if(map[i][j] 0) {//我们假定可以走通map[i][j] 2;if(findWay(map, i 1, j)) { //先走下return true;} else if (findWay(map, i , j 1)){ //右return true;} else if (findWay(map,i - 1, j)) { //上return true;} else if (findWay(map, i , j - 1 )) { //左return true;} else {map[i][j] 3;return false;}} else {return false;}}}
}
当前的地区如下所示
1 1 1 1 1 1 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 1 1 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 1 1 1 1 1 1
找路的情况如下所示
1 1 1 1 1 1 1
1 2 0 0 0 0 1
1 2 2 2 0 0 1
1 1 1 2 0 0 1
1 0 0 2 0 0 1
1 0 0 2 0 0 1
1 0 0 2 2 2 1
1 1 1 1 1 1 1
4.汉诺塔
public class HanoiTower {public static void main(String [] args) {Tower tower new Tower();tower.move(5, A, B, C);}}class Tower {//方法//num 表示要移动的个数 a , b , c 分别表示A塔 B塔 C塔。public void move(int num, char a, char b, char c) {//如果只有一个盘 num 1if (num 1) {System.out.println(a - c);} else {//如果有多个盘,可以看成两个盘, 最下面的和上面的所有盘//1先移动上面所有的盘到b, 借助cmove(num - 1, a, c, b);//(2)把最下面的这个盘 移动到 cSystem.out.println(a - c);//(3)再把 b 塔的所有盘移动到c借助amove(num - 1, b, a, c);}}
}
运行结果
A-C
A-B
C-B
A-C
B-A
B-C
A-C
A-B
C-B
C-A
B-A
C-B
A-C
A-B
C-B
A-C
B-A
B-C
A-C
B-A
C-B
C-A
B-A
B-C
A-C
A-B
C-B
A-C
B-A
B-C
A-C