(整理源于网络)
Java SE 1st day
1、本课程内容:
·本部分所学习到的所有知识,都与开发有直接的联系,而且是最基础的实现;
·在面试之前都会复习此部分,所以在讲解之后会有许多的“面试题”出现;
在JavaSE的课程之中主要分为以下三个部分:
·第一部分:Java SE的基础部分,主要讲解一些基本的程序语法,控制、循环、数组、方法等等;
·第二部分:指的是Java面向对象部分;掌握了此处,以后才没有难点;
·第三部分:Java应用部分,实际上这个就是属于面向对象的应用,或者说类库的使用;
而在整个JavaSE之中,有以下四个核心知识模块必须掌握;
·模块一:面向对象,主要是围绕着抽象类和接口进行讲解;
·模块二:Java集合框架,像数据结构的实现都在此部分;
·模块三:Java IO,进行输入、输出的操作;
·模块四:JDBC,数据库操作,但是此部分比较简单,可是却很重要,因为以后的开发都是基于数据库的;
按照重要性以下几个部分进行一个介绍:
·〖重点〗Java SE基础知识:主要是复习基本的程序逻辑,包括一些环境的搭建等等;
·〖重点〗面向对象:要理解思想,程序的一切抽象来源于生活;
|- 〖重点〗面向对象基础部分:主要是围绕一个了进行的,一些类的关系分析,引用分析等等;
|- 〖重点〗面向对象高级部分:主要是多个关联类之间进行操作,而且会涉及到一些核心概念和设计模式;
|- 〖重点〗异常处理:重点是围绕着异常处理的核心操作格式进行讲解的;
|- 〖重点〗包及访问权限:进行程序的归类;
|- 〖理解〗JDK 1.5新特性:这些新特性不用100%会使,能看懂就行了;
|- 〖重点〗Eclipse开发工具、Power Designer设计:作为辅助的工具使用;
·〖了解〗多线程:在Java SE的学习中以及Java EE的操作之中概念有用,但是实际的效果不明显,但是学习此部分内容是为了以后Android开发做准备的;
·〖重点〗Java类库:要学会查文档,而且一些重要的代码一定要记下来;
·〖重点〗JDBC:讲解通过程序实现的数据表的CRUD操作;
·〖重点〗Java IO:是面向对象的完美体现,一些重要的概念都会在此处体现;
·〖了解〗网络编程:是进行Socket开发的,主要的目的也是为了Android学习准备的;
·〖核心重点〗DAO设计模式:是Java SE课程的总结程序,将使用到之前的所有概念;
在以上的知识点划分之中,对于图形界面是不会有任何涉及的,这个时代已经都过去了;
2、Java 简介
2.1 java 的技术开发主要三个分支:
● J2SE(2005年之后更名为:Java SE):提供的是一些基础的核心支持;
● J2ME(2005年之后更名为:Java ME):是做嵌入式开发的; →被Android
● J2EE(2005年之后更名为: Java EE):作为企业级平台开发的技术;
2.2 java的主要特点(了解)
● java语言足够简单;
● java是一门面向对象的编程语言;
● java避免了C或者C++中的复杂指针操作,而使用了更为简单的引用完成操作;
● java具备了自动的垃圾收集机制,可以更好的进行程序内存的管理;
● java语言具备很强的可移植性;
而Java语言的可移植性主要是在解释上的移植,例如,如下图所示:
每一个程序只找JVM执行,但是不同的OS上提供了不同的JVM,即:只要JVM的解释方式不变,OS可以任意的改变,从而达到可移植性。
面试题:请解释Java实行可移植性的原理:
Java程序最终通过字节码文件运行,运行的时候字节码需要JVM的支持,但是不同的OS上有不同的JVM,程序不关心OS,只关心JVM,只要JVM不改变,程序可以在OS间任意的移植。
2.3 Java开发环境的搭建(重点)
如果要运行Java开发,则首先要使用的是JDK(Java开发工具包)。安装之前首先要进行如下两个准备:
● 关闭防火墙;
● 对于JDK而言,现在是属于多国语言版,即:一个JDK会根据所在语言的不同,显示不同的文字,但是建议显示英文,这样的话比较好翻译,。
修改语言环境:【控制面板】→【管理工具】→【区域和语言选项】
安装完JDK后,会提示安装JRE(Java运行时环境),这个环境主要是为了执行Java程序的时候所使用的,即:如果没有JDK,只有JRE也可以执行Java程序,但并不能编译。
安装后所以的内容都保存在“…\java\”文件夹中,但对于程序开发而言,现在主要使用两个命令:javac、java 。可是这两个命令不属于windows环境中。
把此命令配置到windows环境之中。
● 所有的执行命令的目录:‘…\java\jdk\1.’
● 将目录配置到windows的环境属性之中;
【我的电脑】→【属性】→【高级】→【环境变量】→【编辑Path路径】
Win7配置:
在“环境变量”窗口中的“用户变量”中点击“新建”
变量名:Path
变量值:F:\ProgramFiles\Java\jre7\bin;F:\Program Files\Java\jdk1.7.0_03\bin;
其中第一个路径可以达到:在cmd中运行java(jre,java运行时环境);
第二个路径可以达到:在cmd中运行javac(java编译);
2.4 第一个Java程序(重点)
● 在D盘创建一个“javatest”文件夹
● 建立一个hello.java:
public class hello { public static void main(String args[]){ System.out.println("Hello world"); } } |
● 在cmd中编译hello.java:D:\javatest>javachello.java
编译成功后会在当前目录下创建一个hello.class的字节码文件。
● 运行编译好的class:D:\javatest:>javahello
Hello world |
注意:如果配置环境变量不正确则可能导致编译不成功,如下:
原因:在安装JDK 1.7之前安装了JDK 1.4或更低的版本。
查看JRE版本号:java -version
查看JDK版本号:javac -version
解决方案有两个:
● 方案一:删除掉所有软件(指的是自动安装JDK 1.4的软件,如oracle)中与JDK 1.4有关的配置;
● 方案二:由于path属性采用的是顺序的方式读取,可以将新的配置(指的是JDK&JDR的路径)写在最前面;
修改后关闭cmd在打开后执行查看版本号,如下:
2.5 第一个Java程序的相关解释(重点)
1、关于类的定义,在本程序中写了如下的语句:
public class hello{} |
这条语句的主要功能是用于定义一个类,而且以后所以的代码都是在类中编写的,既然是类,则肯定要有自己的命名要求:每个单词的首字母都要求大写,如:TestHelloDemo,而且对于类的定义也有两种:
● public class:文件名称必须和类的名称保持一致,在一个*.java文件之中只能定义一个public.class;
● class:文件名称可以和类名称不一致,但是执行的时候要执行类名称,在一个*.java文件之中可以定义多个class,但是只能有一个public class,而且不同的定义会分别产生不同的*.class文件;
2、主方法:表示程序的起点:
public static void main(String args[]){} |
此代码表示所以的操作语句都从此开始执行,里面可以进行各个语句的编写。
3、系统输出:直接在屏幕在进行
System.out.println("Hello world"); |
System.out.print("Hello world"); |
在print后有‘ln’表示执行后换行。
2.6 classpath属性(重点)
“classpath”:进行文件路径的指定。例如:现在在“D:\javatest\hello.class”有一个类,如果要执行这个类则肯定要进入到其所在在的文件夹之中。但可以修改classpath以实现在其他盘符或路径下直接执行“D:\javatest\hello.class”中的类。
范例:设置classpath:
SET CLASSPATH=D:\javatest |
设置后就可以在其他路径下直接执行“D:\javatest”下的class文件,如:
E:\>java hello |
但要注意的是,如果classpath到处乱指不方便,所以在默认的情况下,最好让其直接从当前所在的路径中找到所需的*.class文件最合适,那么在开发中一般将其设为默认“.”
SET CLASSPATH=. |
设置classpath的值只会对当前cmd窗口有效,可以通过设置环境变量对所以cmd窗口有效:
关于classpath还有很多相关设置,此处只作一个基本的介绍,为的是说明:使用java命令执行程序的时候,会使用CLASSPATH指定的路径加载*.class文件,并最终在JVM中执行。
面试题:请解释path与classpath的区别?
● path是windows的环境属性,用于指定可执行命令的路径;
● classpath:在java程序执行的时候,用于指定类的加载路径;
2.7关键字和标识符(重点)
在定义变量名称,或类名的时候都需要使用到标识符,在Java之中标识符组成原则如下:由字母、数字、_、$所组成,其中不能以数字开头,而且在开发之中,用户的定义很少使用到“$”,而且不能是java中的保留字(关键字)。
但是这几个关键字之中,有以下几点说明:
● 未使用到的关键字:goto、const;
● 有特殊含义的关键字:null、true、false;
● 在JDK 1.4之后增加了一个assert关键字;
● 在JDK 1.5之后增加了一个enum关键字;
2.8 数据类型(重点)
2.8.1 数据类型的划分(背)
所谓的引用数据类型就是指在C或C++之中的指针,但是比指针更好理解,可是这块内容依然为学习java SE基础部分的难点。对于每个数据类型都有其默认值。
但是从开发的角度而言,以上的这些数据类型以及对应的默认值,不要轻易相信,建议在开发之中,写代码定义变量的时候都要写出默认值。
对于数据类型而言,double数据类型可以装下全宇宙最大的有意义的数字。
常用数据类型:
● int:基本上表示整形的都使用此类型;
● long:一般用于保存日期时间的数字;
● byte:在以后进行二进制数据操作中使用较多;
● double:一个小数就是double类型;
但是一般的移动设备之中,由于其内存较小,所以对于很多的数据类型要求使用最合适的。例如:short能解决的就使用short,float能解决的就使用float。
2.8.2 整形
对于程序而言,默认给出的一个数字,实际上就标识的是int型变量。
整形数据由于存在范围的限制,所以如果计算超过了整形的数据范围,则可能出现数据溢出:
public static void main(String args[]){ int max=Integer.MAX_VALUE; //求出整形的最大值 int min=Integer.MIN_VALUE; //求出整形的最小值 System.out.println("max: "+max); System.out.println("mim: "+min); System.out.println("max+1: "+(max+1)); //最大值加一等于最小值 System.out.println("min-1: "+(min-1)); //最小值减一等于最大值 } |
max: 2147483647 mim: -2147483648 max+1: -2147483648 min-1: 2147483647 |
如果想要解决数据溢出的问题,最直接的做法是扩大数据的范围。比int范围大的是long,所以下面编写代码:
public static void main(String args[]){ int max=Integer.MAX_VALUE; //求出整形的最大值 int min=Integer.MIN_VALUE; //求出整形的最小值 System.out.println("max: "+max); System.out.println("mim: "+min); System.out.println("max+1: "+(max+1L)); System.out.println("min-1: "+(min-1L)); } |
max: 2147483647 mim: -2147483648 max+1: 2147483648 min-1: -2147483649 |
此时,也就发现数据类型的转换操作:
● int → long:由范围小的变到范围大的数据类型,自动进行转换;
● long → int:由范围大的变到范围小的数据类型,强制转换;
范例:大类型转小类型,但超出小类型的数据范围。
public static void main(String args[]){ long a=Integer.MAX_VALUE+1; //定义a为int型最大值+1 int b=(int)a; //将long类型变为int,强制转换 System.out.println(b); } |
-2147483648 |
原因如上述,数据溢出问题。
byte也是一个常见的数据类型,但是byte的范围是“-128~127”,这个数据类型主要用于二进制操作之中,但是在讲解byte的时候有一个注意点:
强制转换 | 直接赋值数字不用强制转换 |
public static void main(String args[]){ int a=10; byte b=(byte)a; System.out.println(b); //需要强制转换 } | public static void main(String args[]){ byte b=10; System.out.println(b); //10是数字,是int型 } |
面试题:请说出以下程序的计算结果:
public static void main(String args[]){ System.out.println(11+1l); } |
12 |
本程序的运行结果是“12”,最终的数据类型是long,记住“l”和“1”不一样,这种题目只是在面试题中会出现,实际的开发之中,都建议把“L”大写,可以减少程序调试的难度。
2.8.3 字符型数据
所谓的字符指的是使用“'”声明的一个单个的内容,字符用char表示,在语言之中,字符和int型数据是可以互相转换的。
public static void main(String args[]){ char a='A'; int b=a; b++; a=(char)b; System.out.println("a="+a+" b="+b); } |
a=B b=66 |
这个的编码和C语言之中是一样的,但是在C语言之中使用的是ASCⅡ编码,可是java的字符使用的是UNICODE编码,而UNICODE编码本身包含了ASCⅡ码。
UNICODE是使用十六进制表示的一种编码格式,可以表示出世界上任何的文字。
面试题:使用char型数据能否表示一个汉字?
public static void main(String args[]){ char a='汉'; System.out.println(a+"\nUNICODE: "+(int)a); } |
汉 UNICODE: 27721 |
不过要注意的是,要想保存汉字,则语言环境必须是中文才可以保存,否则无法保存。
2.8.4 浮点型型数据
浮点型就是小数,但需要注意的是,默认的一个小数的类型是double型数据。
public static void main(String args[]){ double num=100.3; //小数 System.out.println(num*num); //double型数据 } |
10060.09 |
但是,如果说现在要想使用float型数据接收小数,则需要强制转换。
public static void main(String args[]){ float num=100.3F; //小数,(float)100.3 System.out.println(num*1.9); //double型数据 } |
190.57000579833982 |
一般的小数肯定要使用浮点型数据操作,例如,现在要求计算100/8
public static void main(String args[]){ int a=100; int b=8; float result=a/(float)b; System.out.println(a/b+"\n"+result); } |
12 12.5 |
int型数据是没有小数点的,所以要出现小数点的话,则必须将一个数据变为double或float型数据,而且按照数据的自动转换原则:byte → short → long → float → double,即:double是基本数据类型的最大值。
2.8.5 布尔型
在java中存在一种称为boolean型的数据,表示真(true)或假(false),与一般的语言不同,一般的语言之中都会将0当作false,而将非0当作true,可是java中没有这样复杂,boolean型的数据只有true和false两种取值。
public static void main(String args[]){ boolean flag=true; if(flag){ System.out.println("Hello World."); } } |
Hello World. |
3.8.6 字符串:String
字符串在java中使用“"”声明的,通过String型数据保存,但是String由于开头首字母大写,所以肯定是一个类,但是这个类的使用比较特殊而已。
记住了:只要是使用“"”定义的内容都是字符串。
范例:完成加法计算
public static void main(String args[]){ int x=10; int y=20; String result="Calculation: "+x+y; System.out.println(result); } |
Calculation: 1020 |
任何的数据类型,碰到了String型数据之后,都会首先变为字符串,之后再进行后续操作的;
● 转换原则:byte →short → int → long → float → double → String;
String本有许多的特点,包括使用中的注意事项,绝对不属于基本数据类型,但是它的部分使用形式和基本数据类型很相似。
3.9运算符(重点)
以上这些符号的优先级没有记住的必要,因为在以后的开发之中,完全可以利用“()”进行修改,但是不得不反思一下,国内计算机等级考试的题目,经常出现很复杂的不带括号的运算。但在开发中为了维护方便,代码写的越简单越好!
3.9.1 逻辑运算
逻辑运算指的是与、或、非,但在java中与和或有两种变现形式:
● 与:&(普通与)、&&(短路与)
● 或:|(普通或)、||(短路或)
面试题:请解释&和&&、| 和 || 的区别。
● &(普通与)和 |(普通或)指的是所有条件都进行判断;
● &&(短路与)如果前面的条件不满足,则后面不再进行判断,|| (短路或)如果前面的条件满足则后面不再判断;
● 在开发中为了性能的提高,主要用短路与和短路或操作;
● &和除了用于逻辑运算之外,也可以进行位运算的操作;
3.9.2 位运算(了解)
位运算在java的直接开发中出现比较少一些,但是一些加密的程序中会存在位运算的操作,例如:以后要使用的MD5加密方法。
如果要进行位运算,则运算的数据必须是二进制数据,如果现在给出的是一个十进制数据,则需要将其变为二进制数据出现,十进制变为二进制数据的做法:十进制数字除以2,取余数,之后倒着取;
11的二进制数据:00000000 00000000 0000000000001011
11
÷ 2
5 …… 1 ↑
÷ 2
2 …… 1 ↑
÷ 2
1 …… 0 ↑
÷ 2
0 …… 1 ↑
对于位运算而言,在java中有如下几种操作符号:&、|、^、-、<<、>>、>>>;
范例:测试与操作
public static void main(String args[]){ int x=11; int y=5; System.out.println(x&y); } |
1 |
11的二进制数据: 00000000 00000000 00000000 00001011
5的二进制数据: 00000000 00000000 0000000000000101
&操作: 00000000 00000000 00000000 00000001 → 1
范例:测试或操作
public static void main(String args[]){ int x=11; int y=5; System.out.println(x|y); } |
15 |
11的二进制数据: 00000000 00000000 00000000 00001011
5的二进制数据: 00000000 00000000 00000000 00000101
| 操作: 00000000 00000000 00000000 00001111 → 15
面试题:请问,如何可以以最快的速度计算2的3次方
public static void main(String args[]){ int x=2; System.out.println(x<<2); } |
8 |
2的二进制数据: 00000000 00000000 00000000 00000010
左移2位: 00000000 00000000 00000000 00001000 → 8
3.9.3 三目运算符
三目运算符是赋值运算符的一种功能实现,使用如下:
public static void main(String args[]){ int x=10; int y=20; int max=x > y? x:y; System.out.println(max); } |
20 |
在日后的开发之中,三目运算符也会经常使用到。
3.10 程序结构(重点)
从程序的开发角度而言,有三种结构:
● 顺序结构:代码从头到尾顺序执行;
● 分支结构:增加了各种条件的判断操作,使用if、if…else、if…else…if、switch;
● 循环结构:重复执行某些代码结构,使用for、while、do…while;
对于分支结构而言,有一种需要提醒的就是switch操作,switch操作允许判断类型是数字、字符、枚举。并且在使用时,要注意每一个case后面都要加上“break”,否则的话,程序将从第一个满足条件的地方开始陆续的向下继续执行。
循环实际上是作为所有学习编程人员的第一道难关出现,但是对于给出的三种循环,有以下几点参考:
● 大多情况下do…while循环不会被轻易使用;
● while:当不确定循环次数却知道循环的结束条件时使用它;
● for:是在明确知道循环次数的情况下使用的;
● 对于循环的控制也可以使用continue、break;
所有的循环都肯定要有循环的开始条件和结束条件,而且每次循环中必须进行循环条件的修改。
范例:打印乘法口诀表,既然已经知道循环次数,使用for循环最合适
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+" "); } System.out.println(); } } |
1 2 4 3 6 9 4 8 12 16 5 10 15 20 25 6 12 18 24 30 36 7 14 21 28 35 42 49 8 16 24 32 40 48 56 64 9 18 27 36 45 54 63 72 81 |
范例:打印三角形
public static void main(String args[]){ int line=5; //打印5行 for(int i=0;i<line;i++){ for(int j=0;j<=line-i;j++){ System.out.print(" "); } for(int j=0;j<=i;j++){ System.out.print("* "); } System.out.println(); } } |
* * * * * * * * * * * * * * * |
这些基本的程序逻辑题目,只要是有考试都有。
在循环之中使用两个控制语句:
● continue:中断一次;
● break:中断所有循环;
【了解】额外提醒:在C语言中有一个goto语句,表示无条件跳转,但是这个关键字在java中,但是不能使用,可是在java中却可以使用continue实现与goto同样的功能。
范例:比较以下算法,注意区别之处
public static void main(String args[]){ int a[][]={{1},{2,3},{4,5,6},{7,8,9,10}}; pa: for(int i=0;i<a.length;i++){ for(int j=0;j<a[i].length;j++){ if(i==j){ continue pa; } System.out.print(a[i][j]); } System.out.println(); } } | public static void main(String args[]){ int a[][]={{1},{2,3},{4,5,6},{7,8,9,10}}; for(int i=0;i<a.length;i++){ for(int j=0;j<a[i].length;j++){ if(i==j){ continue ;//此处与break一样 } System.out.print(a[i][j]); } System.out.println(); } } |
245789 |
2 45 789 |
但是这种实现的功能是没有太大的用处,所以只是作为语法介绍一下,一般不作使用。
3.11 方法(重点)
3.11.1 方法的基本概念
方法的主要功能是表示一段可以重复调用的代码段,但是在java之中,今天并不能给出一个方法的完整定义格式,只能说,现在由主方法(PS:以后把主方法所在的类称为主类)直接调用的方法定义格式如下:
public static 返回值类型 方法名称(参数列表){ [return[返回值]:]; } |
对于前面说的“public static”现在原样进行编写,而对于返回值类型是java中的各个数据类型,包括:基本数据类型或者是应用数据类型,如果不返回数据,则使用void声明。
范例:定义无参数无返回的方法
public static void main(String args[]){ printMsg(); //由主方法直接调用 } public static void printMsg(){ System.out.println("Hello World."); } |
Hello World. |
而且在定义方法的时候,方法名称也有自己的命名规范:“第一个单词的首字母小写,之后每一个单词的首字母大写”,例如:printInfo,与方法名称定义格式相同的还有变量的名称,例如:studentName;
范例:定义有参数无返回的方法
public static void main(String args[]){ printMsg("Hello World."); //由主方法直接调用 } public static void printMsg(String str){ System.out.println("参数内容= "+str); } |
参数内容= Hello World. |
范例:定义有参数有返回的方法
public static void main(String args[]){ int result=add(10,20); System.out.println(result); System.out.println(add(30,20)); //直接输出方法的返回值 } public static int add(int x,int y){ return x+y; } |
30 50 |
至于说什么时候定义方法,这个就要根据具体的功能实现了,总之有两个最基本的定义方法的原则;
● 第一个:代码需要重复执行的时候就将其定义成方法;
● 第二个:一般主类中的方法中的代码要求越少越好,可以将一些功能在方法中实现;
注意:需要说明的是,在一个操作之中,由于需要接收返回值,所以使用return返回了数据,而且返回的数据类型与方法返回值的类型是完全一样的。如果说现在方法上使用了void声明,也可以使用能够return子句返回,但是返回的不再是内容,而是表示return之后的程序不再执行,返回到方法的调用处上。
这个功能以后的代码之中也会看见,主要是返回到程序调用处,而且前提是:方法的返回值必须是void。
3.11.2 方法的重载
重载(OverLoading),指的是方法名称相同,但是参数的类型或个数不同的情况,例如,现在要求定义方法,方法可以满足两个类型数据的相加、两个小数的相加、三个整形数据的相加操作,那么如果不使用重载概念,定义如下:
public static void main(String args[]){ System.out.println(add1(10,20)); System.out.println(add2(10.2,20.9)); System.out.println(add3(10,20,30)); } public static int add1(int x,int y){ return x+y; } public static double add2(double x,double y){ return x+y; } public static int add3(int x,int y,int z){ return x+y+z; } |
30 31.099999999999998 60 |
现在的功能确实实现了,但是这种实现有问题,在调用方法之前还必须知道方法的编号。
这样对于方法的操作一是方便调用,二是不方便程序的维护,所以此时就可以利用重载的概念完成。
public static void main(String args[]){ System.out.println(add(10,20)); System.out.println(add(10.2,20.9)); System.out.println(add(10,20,30)); } public static int add(int x,int y){ return x+y; } public static double add(double x,double y){ return x+y; } public static int add(int x,int y,int z){ return x+y+z; } |
30 31.099999999999998 60 |
以后会根据参数的类型或个数调用不同的方法体完成不同的功能。
范例:观察以下程序
System.out.println(1); //打印int System.out.println('A'); //打印char System.out.println(10.2); //打印double System.out.println("String"); //打印String System.out.println(true); //打印boolean |
可以发现现在一个“System.out.println()”语句可以打印不同的数据类型,所以这个方法肯定被重载了。
但是对于重载有以下两点说明:
● 方法重载的时候是针对参数的类型和个数的不同,而不是针对返回值的不同,例如,如下的操作就属于错误的重载:
public static int add(int x,int y){ return x+y; } public static double add(int x,int y){ return x+y; } |
● 方法重载的时候理论上返回值类型可以不一样,但是建议不要这么做,建议方法重载的时候返回值的类型最好统一,要么是所有的类型都一样,要么就是都会返回数据,而不是说有的返回数据,有的不返回数据。
3.11.3 方法的递归调用(理解)
递归调用指的就是一个方法调用自己的情况,但是如果要进行递归调用的话,有以下几个前提:
● 递归调用一定发生在方法上;
● 递归调用的时候一定有结束条件,并且每次操作都要去修改这个结束的判断条件。
范例:使用递归完成数据的累加操作
public static void main(String args[]){ System.out.println(add(100)); //此程序如果输入负数,则会陷入死循环 } public static int add(int num){ if(num==1){ return 1; } return num+add(num-1); } |
5050 |
对于递归调用有以下建议:
● 递归调用操作如果操作不好则有可能会出现内存溢出的问题,所以尽量不要使用;
● 部分递归操作都可以使用循环完成,只是代码结构不好,所以递归不是唯一的选择;
● 掌握递归操作需要时间的积累,而且递归操作需要多写代码,在需要使用递归的时候再使用递归;
3.12 数组(重点)
引用数据类型有三种:数组、类、接口,那么讲解数组,主要是为了引出引用数据类型的概念,但从实际的开发来看,数组的使用已经不多了。
3.12.1 数组的基本概念
定义格式一:声明并开辟数组,关键字new表示的就是开辟内存空间,即:引用传递的基础
数据类型 数组名称 [ ] = new 数据类型 [长度] |
数据类型 [ ] 数组名称 = new 数据类型 [长度] |
定义格式二:分开声明
声明数组: | 数据类型 [ ] 数据名称 = null |
开辟数组空间: | 数组名称 = new 数据类型 [长度] |
可以发现,不管是哪个格式,永远都必须使用关键字new进行空间的开辟。
数组开辟之后肯定需要用户访问,访问的格式是“数组名称【索引】”,而且索引的下标是从0开始的,即:如果是10个元素的数组,其下标的范围是0~9,如果超过了这个范围,则会出现越界的异常;
当数组开辟空间之后,数组中的每一个元素的内容都是其数据类型的默认值;
而且对于数组的输出,建议的操作形式是采用循环,但是一般都用for循环,如果是for循环的话,一般都需要知道循环的次数,所以可以使用“数组.length”求出数组的长度。
范例:定义数组
public static void main(String args[]){ int []data=new int [6]; int i=0; for(;i<data.length;i++){ data[i]=i; } printStr(data,i); } public static void printStr(int num[],int i){ for(int j=0;j<num.length;j++){ System.out.print(num[j]+"\t"); } } |
0 1 2 3 4 5 |
明白了数组的基本操作之后,下面来看一下何为引用传递,首先要知道的是:java语言本身的内存没有像书所的那么复杂,就一块内存,但是回来人们为了分析方便,所以将C++的内存分析机制引入到java之中,所以以后也按照这种方式进行内存的分析,但是必须记住,java的内存就一块,下面先给出两块分析内存:
● 堆内存:Heap memory,是保存数组中的具体内容的空间;
● 栈内存:Stack memory,是保存一块推内存的引用地址,但是为了理解方便,可以简单的理解为,保存一个数组名称;内存关系图如下:
之前的操作是将数组的定义和内存的开辟在一条语句找那个完成了,下面分为两步操作:
public static void main(String args[]){ int []data=null; //声明数组 data=new int[6]; //开辟数组空间 int i=0; for(;i<data.length;i++){ data[i]=i; } printStr(data,i); } public static void printStr(int num[],int i){ for(int j=0;j<num.length;j++){ System.out.print(num[j]+"\t"); } } |
0 1 2 3 4 5 |
通过代码可以发现,不管使用的是哪种数组的定义格式,永远都有一个前提必须把握,数组必须开辟空间(使用关键字new)之后才可以使用,而且每一个堆内存的地址都在栈中保存。
那么所谓的引用传递呢?实际指的是“一块堆内存同时被多个栈内存所指向的情况”。
public static void main(String[] args) { int[] data = null; // 声明数组 data = new int[6]; // 开辟数组空间 int i = 0; for (; i < data.length; i++) { data[i] = i; } int arr[] = data; arr[0] = 8000; System.out.println(arr[0]); } |
8000 |
记住,按照之前的分析“data”不是一个具体的内容,而是保存了堆的地址,所以此处的“int arr[ ]=data”意思是将data所指向的堆内存的地址给了arr,即:一块堆内存被两个栈内存所指向。
引用传递的核心就在于:一块堆内存被多块栈内存所指向,一个栈内存修改了内容,其他的所有对应的栈内存的数据都要进行修改。
3.12.2 数组的静态初始化
在之前的程序中所定义的数组,都是属于数组的动态初始化操作,即:开辟空间之后再为其设置内容,数组本身也有静态初始化,即:可以在数组定义的时候就为其指定具体的内容,格式如下:
数据类型 [ ] 数据名称 = {值1,值2,…}; |
数据类型 [ ] 数据名称 = new 数据类型 [ ]{值1,值2,…}; |
唯一所需要注意的是,“new 数据类型 [ ]{值1,值2,…}”,这样编写是为了让以后的一些概念简化。
3.12.3 二维数组(了解)
之前使用的是一维数组,因为只有一个“[ ]”,二维是“[ ][ ]”,更高维的数组基本上就不考虑,如果说一维数组是一个列的话,那么二维数据就是一个表;
二维数组本身的定义格式如下:
格式一:动态初始化
数据类型 [ ][ ] 数据名称 = new 数据类型[长度][长度]; |
数据类型 数据名称 [ ][ ] = new 数据类型[长度][长度]; |
格式二:静态初始化
数据类型 [ ][ ] 数组名称 = new 数据类型[ ][ ]{{值1,值2…},{值1,值2…}…}; |
二维数组就是数组中包含了数组。
3.12.4 数组和方法(核心)
在之前所讲解的方法都是接收了一些基本数据类型,但是方法本身也可以接收或返回引用数据类型,而数组也可以这样操作,方法之中的数组的传递,完全符合于之前讲解的数组的引用传递操作。
public static void main(String args[]){ int []data=new int[6]; int i=0; for(;i<data.length;i++){ data[i]=i; } printStr(data); } public static void printStr(int num[]){ for(int x=0;x<num.length;x++){ System.out.print(num[x]+"\t"); } } |
0 1 2 3 4 5 |
既然方法中的数组传递与之前的引用传递类似,那么就证明在方法之中,可以修改数组的内容。
范例:完成数组的排序(重点)
public static void main(String args[]){ int data[]=new int[]{23,49,12,5,92,16,87}; sort(data); printStr(data); } public static void printStr(int num[]){ for(int x=0;x<num.length;x++){ System.out.print(num[x]+"\t"); } } public static void sort(int a[]){ for(int i=0;i<a.length-1;i++){ for(int j=0;j<a.length-1-i;j++){ if(a[j]>a[j+1]){ int temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; } } } } |
5 12 16 23 49 87 92 |
既然方法可以接收数组,那么也可以让一个方法返回数组,此时,只需要在返回值类型上明确的表示是数组即可。
public static void main(String args[]){ int data[]=init(); sort(data); printStr(data); } public static void printStr(int num[]){ for(int x=0;x<num.length;x++){ System.out.print(num[x]+"\t"); } } public static void sort(int a[]){ for(int i=0;i<a.length-1;i++){ for(int j=0;j<a.length-1-i;j++){ if(a[j]>a[j+1]){ int temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; } } } } public static int[] init(){ int arr[]=new int[]{23,49,12,5,92,16,87}; return arr; } |
5 12 16 23 49 87 92 |
对于方法接收和返回的数据,就看接收和返回的数据类型即可,如果是数组,就定义成相关类型的数组。
3.12.5 与数组有关的操作方法(重点)
为了简化用户的开发,在java之中实际上也堆数组有了更多的操作支持,有两个方法比较常用:排序、数组拷贝。
1、数组排序:
如果现在要为数组排序,在java中提供了一个操作的支持,格式:“java.util.Arrays.sort(数组名称)”;
范例:使用sort排序
public static void main(String args[]){ int data[]={23,78,96,48,76,36,10}; java.util.Arrays.sort(data); printStr(data); } public static void printStr(int num[]){ for(int x=0;x<num.length;x++){ System.out.print(num[x]+"\t"); } } |
10 23 36 48 76 78 96 |
如果在面试之中,出现让你完成“排序程序”实现的话,不能直接写这样的代码,应该先写出之前的程序,最后可以补充一句,使用“java.util.Arrays.sort(数组名称)”也可以排序。2、数组拷贝:
数组的拷贝操作指的是可以将一个数组的部分内容拷贝到另外一个数组之中,格式如下:
● System.arraycopy(源数组名称,源数组开始点,目标数组名称,目标数组开始点,拷贝长度)
public static void main(String args[]){ int dataA[]={0,1,2,3,4,5,6,7,8,9}; int dataB[]={00,11,22,33,44,55,66,77,88,99}; System.arraycopy(dataB, 2, dataA, 3, 4); printStr(dataA); } public static void printStr(int num[]){ for(int x=0;x<num.length;x++){ System.out.print(num[x]+"\t"); } } |
0 1 2 22 33 44 55 7 8 9 |
这两个方法在开发之中比较常用,建议好好熟悉一下,多加练习。
3、总结
1、java的数据类型划分及默认值;
2、public class和class定义的区别;
3、Path和classpath的区别;
4、基本的程序操作语法;
5、方法及方法的重载必须100%掌握;
6、数组的相关内容及相关操作方法;
4、作业
1、完成1!+2!+……60!
DIY |
public static void main(String args[]){ int a=1; double sum=0; for(;a<=60;a++){ sum=cumulation(a)+sum; } System.out.println(sum); } public static double cumulation(int data){ double result=1; for(int x=1;x<=data;x++){ result=result*x; } return result; } |
8.46206204346806E81 |
Answer |
实现一:通过循环完成 |
public static void main(String args[]){ double sum=0; for(int x=1;x<=60;x++){ double temp=1; for(int y=1;y<=x;y++){ temp*=y; } sum+=temp; } System.out.println(sum); } |
实现二:通过递归来完成 |
public static void main(String args[]){ System.out.println(mul(60)); } public static double mul(int num){ if(num==1){ return fun(1); } return fun(num)+mul(num-1); } public static double fun(int num){ if(num==1){ return 1; } return num*fun(num-1); } |
2、编写一个方法,此方法可以将一个整形变为二进制输出(此处不考虑负数)。
DIY |
import java.util.ArrayList;
public class One { public static void main(String args[]) { ArrayList<String> result = BinarySystem(11); for (int i = result.size() - 1; i >= 0; i--) { System.out.print(result.get(i)); } }
public static ArrayList<String> BinarySystem(int data) { ArrayList<String> store = new ArrayList<String>(); int remainder; while (data != 0) { remainder = data % 2; if (remainder == 1) { store.add("1"); } else store.add("0"); data = data / 2; } return store; } }
|
1011 |
Answer |
实现一:通过循环完成 |
public static void main(String args[]) { int num=11; String bin=""; //保存转换的结果 while(num!=0){ //表示还有数字,可以继续执行计算 bin=num%2+bin; //保存结果 num=num/2; //改变num内容 } System.out.println(bin); } |
注意:String的连接运算。bin=num%2+bin与bin=bin+num%2是不同的结果。 |
实现二:通过递归完成 |
public static void main(String args[]) { ToBinary(11); } public static void ToBinary(int num){ if(num/2==0){ System.out.print(num%2); }else { ToBinary(num/2); System.out.print(num%2); } } |
3、有5个人坐在一起,问第5个人多少岁?答:比第4个大2岁,问第4个人多少岁?比第3个人大2岁,问第2个人多少岁?比第1个大2岁,第1个人8岁。
DIY |
public static void main(String args[]) { ComputeAge(8,5); } public static void ComputeAge(int age,int num){
if(num==1){ System.out.println(age); }else { age=age+2; num--; ComputeAge(age,num); } } |
16 |
Answer |
实现一:循环 |
public static void main(String args[]) { int age =8; for(int i=1;i<5;i++){ age+=2; } System.out.println(age); } |
实现二:递归 |
public static void main(String args[]) { System.out.println(age(5)); } public static int age(int num){ if(num==1){ return 8; }else { return 2+age(num-1); } } |
4、将一个给定的整形数组转置输出。
提示:要想实现数组的转置操作,最方便的方法是取中间之后进行依次的替代。
1 2 3 4 5 6
6 2 3 4 5 1
6 5 3 4 2 1
6 5 4 3 2 1
Answer |
public static void main(String args[]) { int data []=new int[10]; init(data); reverse(data); print(data); } public static void init(int arr[]){ for(int i=0;i<arr.length;i++){ arr[i]=i; } } public static void print(int arr[]){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+" "); } } public static void reverse(int arr[]){ int center=arr.length/2; //求出数组的中间点 int head=0; //从头开始据算下标 int tail=arr.length-1; //从尾开始计算下标 for(int i=0;i<center;i++){ int temp=arr[head]; arr[head]=arr[tail]; arr[tail]=temp; } } |
9 1 2 3 4 5 6 7 8 0 |
本程序在以后的课程讲解中肯定要使用,一定要理解代码的实现,而且在实现本程序的时候,唯有的思路,就在于应该将主方法中的代码变的越少越好。
5、现在有如下的一个数组:
Int oldArr[ ]={1,3,6,4,0,76,43,15,0,0,6,8,0};
要求将以上数组中值为0的项去掉,将不为0的值存入一个新的数组,生成的新数组为:
int newArr[ ]={1,3,6,4,76,43,15,6,8};
DIY |
public static void main(String args[]) { int oldArr[]={1,3,0,45,9,0,0,45,5,0}; DeleteZero(oldArr); } public static void DeleteZero(int arr[]){ int length=0; int newArr[]; for(int i=0;i<arr.length;i++){ if(arr[i]!=0){ length++; } } newArr=new int[length]; for(int i=0,j=0;j<length;i++){ if(arr[i]!=0){ newArr[j]=arr[i]; System.out.print(newArr[j]+"、"); j++; } } } |
1、3、45、9、45、5、 |
Answer |
实现思路: 1、应该知道不为0的数字的个数,并且以此个数开辟一个新的数组; 2、将旧数组中不为0的内容拷贝到新数组之中; |
public static void main(String args[]) { int oldArr[]={1,3,0,45,9,0,0,45,5,0}; int newArr[]=new int[count(oldArr)]; copy(oldArr,newArr); print(newArr); } public static int count(int arr[]){ int sum=0; for(int i=0;i<arr.length;i++){ if(arr[i]!=0){ sum++; } } return sum; } public static void copy(int arrA[],int arrB[]){ int foot=0; //操作arrB的脚标 for(int x=0;x<arrA.length;x++){ if(arrA[x]!=0){ arrB[foot++]=arrA[x]; } } } public static void print(int arr[]){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+"、 "); } } |
1、 3、 45、 9、 45、 5、 |
6、现在给出两个数组:
● 数组A:“1,7,9,11,13,15,17,19”;
● 数组B:“2,4,6,8,10”
两个数组合并为数组C,按升序排序。
DIY |
public static void main(String args[]) { int arrA[]={1,3,5,7,9,11,13,15}; int arrB[]={2,4,6,8,10,12,14}; MergerSort(arrA,arrB); }
public static void MergerSort(int arrA[],int arrB[]){ int arrC[]; arrC=new int[arrA.length+arrB.length]; System.arraycopy(arrA, 0, arrC, 0, arrA.length); System.arraycopy(arrB, 0, arrC, arrA.length, arrB.length); java.util.Arrays.sort(arrC); print(arrC); }
public static void print(int arr[]){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+"、 "); } } |
1、 2、 3、 4、 5、 6、 7、 8、 9、 10、 11、 12、 13、 14、 15 |
Answer |
public static void main(String args[]) { int dataA[]=new int[]{1,3,5,7,9,11,13,15}; int dataB[]=new int[]{2,4,6,8,10,12,14}; int dataC []=contact(dataA,dataB); java.util.Arrays.sort(dataC); print(dataC); }
public static int[] contact(int arrA[],int arrB[]){ int arrC[]=new int[arrA.length+arrB.length]; System.arraycopy(arrA, 0, arrC, 0, arrA.length); System.arraycopy(arrB, 0, arrC, arrA.length, arrB.length); return arrC; }
public static void print(int arr[]){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+"、 "); } } |
本题目的训练目的就在于将两个操作的方法熟悉了,但是本程序以后在讲解中依然会使用到,重点是理解思路。
4.1参考答案:
1、
实现一:通过循环完成
public static void main(String args[]){ double sum=0; for(int x=1;x<=60;x++){ double temp=1; for(int y=1;y<=x;y++){ temp*=y; } sum+=temp; } System.out.println(sum); } |
实现二:通过递归来完成
public static void main(String args[]){ System.out.println(mul(60)); } public static double mul(int num){ if(num==1){ return fun(1); } return fun(num)+mul(num-1); } public static double fun(int num){ if(num==1){ return 1; } return num*fun(num-1); } |
2、(此处不考虑负数)
实现一:通过循环完成
public static void main(String args[]) { int num=11; String bin=""; //保存转换的结果 while(num!=0){ //表示还有数字,可以继续执行计算 bin=num%2+bin; //保存结果 num=num/2; //改变num内容 } System.out.println(bin); } |
注意:String的连接运算。bin=num%2+bin与bin=bin+num%2是不同的结果。
实现二:通过递归完成
public static void main(String args[]) { ToBinary(11); } public static void ToBinary(int num){ if(num/2==0){ System.out.print(num%2); }else { ToBinary(num/2); System.out.print(num%2); } } |
3、
实现一:循环
public static void main(String args[]) { int age =8; for(int i=1;i<5;i++){ age+=2; } System.out.println(age); } |
实现二:递归
public static void main(String args[]) { System.out.println(age(5)); } public static int age(int num){ if(num==1){ return 8; }else { return 2+age(num-1); } } |
4、
public static void main(String args[]) { int data []=new int[10]; init(data); reverse(data); print(data); } public static void init(int arr[]){ for(int i=0;i<arr.length;i++){ arr[i]=i; } } public static void print(int arr[]){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+" "); } } public static void reverse(int arr[]){ int center=arr.length/2; //求出数组的中间点 int head=0; //从头开始据算下标 int tail=arr.length-1; //从尾开始计算下标 for(int i=0;i<center;i++){ int temp=arr[head]; arr[head]=arr[tail]; arr[tail]=temp; } } |
9 1 2 3 4 5 6 7 8 0 |
本程序在以后的课程讲解中肯定要使用,一定要理解代码的实现,而且在实现本程序的时候,唯有的思路,就在于应该将主方法中的代码变的越少越好。
5、
实现思路:
1、应该知道不为0的数字的个数,并且以此个数开辟一个新的数组;
2、将旧数组中不为0的内容拷贝到新数组之中;
public static void main(String args[]) { int oldArr[]={1,3,0,45,9,0,0,45,5,0}; int newArr[]=new int[count(oldArr)]; copy(oldArr,newArr); print(newArr); } public static int count(int arr[]){ int sum=0; for(int i=0;i<arr.length;i++){ if(arr[i]!=0){ sum++; } } return sum; } public static void copy(int arrA[],int arrB[]){ int foot=0; //操作arrB的脚标 for(int x=0;x<arrA.length;x++){ if(arrA[x]!=0){ arrB[foot++]=arrA[x]; } } } public static void print(int arr[]){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+"、 "); } } |
1、 3、 45、 9、 45、 5、 |
6、
public static void main(String args[]) { int dataA[]=new int[]{1,3,5,7,9,11,13,15}; int dataB[]=new int[]{2,4,6,8,10,12,14}; int dataC []=contact(dataA,dataB); java.util.Arrays.sort(dataC); print(dataC); }
public static int[] contact(int arrA[],int arrB[]){ int arrC[]=new int[arrA.length+arrB.length]; System.arraycopy(arrA, 0, arrC, 0, arrA.length); System.arraycopy(arrB, 0, arrC, arrA.length, arrB.length); return arrC; }
public static void print(int arr[]){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+"、 "); } } |
本题目的训练目的就在于将两个操作的方法熟悉了,但是本程序以后在讲解中依然会使用到,重点是理解思路。