文章目录
一、Java入门
Java开发环境
- JDK: Java Development ToolKit :Java开发工具包.
- JDK下载安装:安装JDK时,记住安装目录。
- JDK配置(配置环境变量):
- PATH:把JDK安装目录下的bin目录(此电脑->右键属 性->高级系统设置->环境变量)
- JAVA_HOME:JDK的安装目录
- CLASS_PATH:类路径(把JDK中的Java类库或者第三方的Java类库所在目录配置进来)
第一个Java程序
Java程序开发步骤:
-
第一步:编写源代码
新建源文件,后缀名是.java
文本编辑器:记事本,notepad++,IDEpublic class HelloWorld{ public static void main(String[] args){ System.out.println("Hello World!"); } }
文件名要求和文件中public修饰的类的类名相同。(如果文件中没有public修饰的类,那么文件名就没有要求)
建议类名首字母大写 -
第二步:编译源代码
使用编译器javac对源代码进行编译:javac 源文件路径
编译出的结果:Xxx.class字节码文件, xxx就是class后边的类名,字节码文件是以类为单位的,也就是一个类对应一个字节码文件
-
第三步:加载执行
使用解释器java对源码进行加载,虚拟机进行解释执行:java Xxx
java解释器后边时main方法所在的类的类名
二、变量和数据类型
1.注释
注释不会执行,是一些解释说明信息。Java中的注释在编译的时候就会被忽略掉。
Java支持三种注释:
-
单行注释
从注释符号开始,到当前行的末尾都是注释。
//这后边都是注释
-
多行注释
多行注释包含在/* */之间.
/* 这里边是多行注释 */
-
文档注释
文档注释是一种特殊的多行注释,主要对Java中的类、方法、成员变量等进行说明。文档注释会包含在javadoc生成的文档中。
/** 这里是文档注释 */
一般为了更美观,建议文档 注释中每行注释前边都加一个星号。
/** * 这是一行注释 * 这又是一行注释 */
2.变量
变量就代表内存中的一块存储空间,可以用来存取数据。
Java中把数据做了分类,不同类型的数据需要的存储空间大小不一样,这就要求变量必须指定数据类型。数据类型决定了变量分配多大的内存空间。
2.1 声明变量
变量声明以后,才可以使用。变量的声明包含两部分:数据类型和变量名。
数据类型 变量名;
-
变量名
Java中变量名、类名、方法名等等这些有程序员设置的名字叫做标识符。
Java中标识符要求:
- 只能是字母(中文文字)、数字、下划线和美元符号(人民币符号)
- 不能以数字开头
- 不同与Java关键字同名(public class static)
- 见名知意
- 多个单词的话,通过单词首字母大写的驼峰式或者用下划线隔开. studentName student_name
- 建议变量名和方法名首字母小写;类名首字母大写
-
数据类型
int age;
通过变量名age就可以进行数据的存取.
变量如果没有声明,直接使用会报错:
VariableDemo.java:6: 错误: 找不到符号
age = 18;
^
符号: 变量 age
位置: 类 VariableDemo
2.2 变量初始化
初始化就是给变量第一次赋值。Java要求变量声明以后,必须先初始化,才能继续使用。
变量 = 值;
没有初始化,直接使用,会报错:
VariableDemo.java:8: 错误: 可能尚未初始化变量age
age = age + 1;
^
1 个错误
2.3 使用变量
变量的使用无非就是存数据和取数据。
-
存(修改)
变量 = 值;
-
取
变量 + 1
变量名直接就可以代表变量中的数据
public class VariableDemo {
public static void main (String[] args) {
//1.声明变量
int age;
//2.初始化
age = 18;
//3.使用
age = age + 1;
//输出
System.out.println("年龄:" + age);
System.out.println("======================");
//Java支持变量声明的同时初始化
int num1 = 50;
System.out.println("1班的学生人数是" + num1);
//Java支持可以一条语句声明多个同类型的变量
int num2=60,num3=70;
System.out.println("2班的学生人数是" + num2);
System.out.println("3班的学生人数是" + num3);
}
}
2.4 常量
常量相对于变量的区别:
-
声明的时候在数据类型前边加上final
-
常量初始化以后,再不允许修改(赋值)
-
常量名建议全大写,多个单词下划线隔开
final 数据类型 常量名;
final int SIDE = 60;
如果常量初始化以后再次修改,会报错:
VariableDemo.java:23: 错误: 无法为最终变量SIDE分配值
SIDE = 70;
^
1 个错误
Java中把10、1.5、true、‘男’、”中国“ 等字面量称为字面值常量
3.数据类型
数据类型分为基本数据类型和引用数据类型,这里研究基本数据类型和String字符串类型。
基本数据类型总共8个,分为四大类。
3.1 整形
整形包含四个:int long short byte
-
int
基本整形,占据4字节(B),32位(b), 负的2的31次方 到 正的2的31次方-1
-
long
长整形,占据8字节(B),64位(b), 负的2的63次方 到 正的2的63次方-1
-
short
短整型,占据2字节(B),16位(b), 负的2的15次方 到 正的2的15次方-1 -32768 到 32767
-
byte
字节,常用于字节数据流处理
3.2浮点型
浮点型2种:float double , 浮点型不精确,只能控制精度。
-
float
浮点,4字节,32位
-
double
双精度浮点 , 8字节,64位
3.3字符型
字符型:char
字符型存储的是单个字符,需要使用单引号标识(单引号 引起来)
'中' 'a' '1'
字符中包含一些特殊字符,我们一般也叫作转义字符。
-
不能使用一个符号进行展示的字符
'\n' '\r' '\t'
换行符 回车符 制表符
-
被赋予了特殊意义的字符,不能再代表自己的字符
'\'' '\"' '\\'
单引号 双引号 反斜线
字符会被编码为二进制码(16)进行存储。所以可以把char类型的字符当做16位的一个整数进行使用(运算)。
3.4布尔型
布尔型:boolean
boolean类型只有两个数据:true、false
3.5字符串类型
字符串类型String不是基本数据类型,是引用数据 类型,但是String的使用非常多,而且跟基本类型相似。
字符串中包含多个字符(大于等于0),双引号标识。
"" "中" "中国"
public class DataTypeDemo {
public static void main (String[] args) {
String name = "Java高级程序设计";//商品名
int count = 100;//商品数量
float price = 245.98f;//商品价格
char flag = '中';//标记
boolean isHot = true;//是否热卖品
//输出
System.out.println("商品信息如下:");
System.out.println("商品名称:" + name);
System.out.println("商品数量:" + count);
System.out.println("商品价格:" + price);
System.out.println("商品标志:" + flag);
System.out.println("是否热卖:" + isHot);
//特殊字符
System.out.println("换行符:" + '\n');
System.out.println("回车符:" + '\r');
System.out.println("制表符:");
System.out.println("张飞"+'\t'+"张翼德");
System.out.println("关羽"+'\t'+"关云长");
System.out.println("李世民"+'\t'+"太宗");
System.out.println("字符a+1是" + (char)('a'+1) );//字符b
}
}
4.数据类型转换
4.1 字面值的类型
12 3.14
默认整形字面值当做int处理,小数字面值当做double处理。
12L 3.14F
如果整形字面值后边加个l/L,那么就当做long类型处理。小数后边加个f/F,那么 就当做float类型处理 。
注意:如果整数字面值的大小超过int的范围,就必须加上l/L。
4.2 数据类型转换
数据类型转换:自动类型转换 和 强制类型转换
-
自动类型转换
有些类型之间转换时会自动进行。
根据能存储数据的范围大小进行排序,从小到大(左到右)会自动转换,从大到小需要强制转换.
byte short (char) int long float double
-
强制类型转换
强制转换在要转换的数据之前加上小括号,小括号里边是目标类型。
int age = 30; short sAge = (short)age;
public class TypeConverterDemo {
public static void main (String[] args) {
//自动类型转换
short num1 = 20;
int num2 = 80;
long num3 = 1200;
float num6 = 3.14F;//F作为float类型数据的标志
double num4 = 350.99;
num2 = num1;//short --> int
num4 = num3;//long --> double
//强制类型转换
num2 = (int)num4;//double --> int 需要强制转换
float num5 = (float)1.5;//double ---> float
long num7 = 10000000000L;//L作为long类型数据的标志
}
}
注意:做运算时,比int低的类型先自动上升为int类型,然后再进行运算。也就是运算结果最低都是int类型。
byte b = 1;
short s = 2;
int i = b + s;//结果是int类型
三、Java中的运算符
1.算术运算符
主要对数值型进行运算。
+ - * / %
都是双目运算符,也就是有两个操作数。同类型进行运算,如果不同类型,默认把小类型上升为大类型。结果还是这种类型。
注意:
- 除法/ 要注意结果是同类型的问题。 1/2的结果是0
- 取余% 也叫作取模。取余运算的结果是有规律的(跟除数有关)。 给a取余,结果是0到(a-1)。
- 取余可以用来判断整除(余数是0)
- 约束数据的范围 [m,n] [0,n-m] + m %(n-m+1) + m
++ --
自增自减运算符。单目运算符(只有一个操作数),但是要求唯一的操作数必须是变量。因为自增(减)运算是给变量加(减)1。
++a --a a++ a--
等价于
a = a + 1;
a = a - 1;
前自增(减) 和 后自增(减) 的区别:
-
前后的相同点:都会给变量加(减)1
-
前后的区别主要体现在运算结果不一样
int r = ++a; int r2 = a++;
- 前自增(减):先给变量加(减)1; 然后取变量中的值作为运算结果.
- 后自增(减):先取变量中的值作为运算结果; 然后给变量加(减)1.
public class SelfAddDemo {
public static void main (String[] args) {
//自增自减运算
int num1 = 10;
int num2 = 10;
System.out.println("自增之前:num1="+num1+",num2="+num2);
int r1 = ++num1;
int r2 = num2++;
System.out.println("自增之后:num1="+num1+",num2="+num2);//11,11
System.out.println("自增结果:r1(前)="+r1+",r2(后)="+r2);//10,11
int num3 = 10;
int r3 = num3++ + --num3 - num3-- + ++num3;
//num3 11 10 9 10
//结果 10 10 10 10
System.out.println("num3="+num3);//10
System.out.println("r3="+r3);//20
}
}
2.赋值运算符
赋值运算符主要用来给变量赋值。
a = 1;
把右边的值赋值给左边的变量,要求左边必须是变量。
组合赋值运算符:把算术运算符(位运算符) 和 赋值运算符进行组合,先做算术运算,再做赋值运算。
+= -= *= /= %=
a += 1; 等价于 a = a + 1;
public class SwapDemo {
public static void main (String[] args) {
//交换两个变量的值
int a=1,b=2;
System.out.println("交换之前:a="+a+",b="+b);
int tmp = a;
a = b;
b = tmp;
System.out.println("交换之后:a="+a+",b="+b);
//可以借助算术运算实现交换(运算中产生的数据不能超过int范围)
a += b;
b = a - b;
a -= b;
System.out.println("再交换之后:a="+a+",b="+b);
}
}
3.关系运算符
关系运算表述两个数据的某种大小关系是否成立。成立结果是true;否则结果是false;
> < >= <= == !=
a > b
a == b
注意:
- 关系运算只针对于基本数据类型的数据。(字符串String类型的数据不要用)
- 运算结果是boolean类型
4.逻辑运算符
逻辑运算是表述两个boolean类型之间的逻辑关系。
&& || ! ^(异或) & |
注意:
- 逻辑运算只能操作boolean类型的数据
- 逻辑运算的结果还是boolean
&&(与) | true | false |
---|---|---|
true | true | false |
false | false | false |
||(或) | true | false |
---|---|---|
true | true | true |
false | true | false |
^(异或) | true | false |
---|---|---|
true | false | true |
false | true | false |
关于逻辑与和逻辑或的短路特性: && || 有短路特性;& | 没有短路特性
- 建议使用带短路特性的&& ||
- 短路:左边的操作数如果能决定最终的运算结果,那么右边不做运算。 对&&来说,左边是false,会短路;对||来说,左边是true,会短路。
5.条件运算符
条件运算符作用: 根据条件的真假,从两个数据中选择一个作为结果。
条件?数据1:数据2
结果: 条件为true,数据1作为结果; 否则以数据2作为结果。
注意:
- 条件必须是boolean,在问号前边
- 数据1和数据2必须同类型,处于冒号两边
public class OperatorDemo3 {
public static void main (String[] args) {
//从两个整数找出较大的
int a=10,b=30;
int max = a>b?a:b;
System.out.println(a+"和"+b+"中较大的是:" + max);
//处理非法值:成绩中负数(非法值)都当做0处理.
int score = -1;
score = score>=0?score:0;
System.out.println("成绩是:" + score);
//总共有total行数据,每页可以显示size行,计算总共需要多少页才能全部显示
int total = 100;
int size = 10;
int pages = total/size;
pages = total%size==0?pages:pages+1;
System.out.println("每页"+size+"行,"+total+"行需要" + pages + "页");
}
}
6.位运算符
对数据的二进制位直接进行运算。
&(位与) |(位或) ^(位异或) ~(位取反) <<(左移) >>(右移:空出来的位置补符号位) >>>(右移,空出来的位置补0)
四、选择结构
Java中的流程控制结构:
-
顺序结构
代码按照从上往下的顺序一行行执行.
-
选择结构
根据条件的真假,选择其中一个分支的代码块执行.
-
循环结构
控制代码块循环多次执行,条件控制循环的次数.
1.系统的输入输出
1.1 系统输入System.in
Java中的系统输入默认绑定的是键盘输入缓冲器,系统输入可以从缓冲器读取键盘输入的数据。
直接使用System.in读取键盘输入的数据比较麻烦,Java提供了Scanner这个工具,可以封装系统输入,更简单方便。
Scanner读取键盘输入的步骤:
- 导入Scanner
- 实例化Scanner对象
- 调用Scanner对象的nextXxx方法读取数据
- nextInt
- nextLong
- nextShort
- nextByte
- nextFloat
- nextDouble
- nextBoolean
- next 字符串String
- nextLine 读一行,遇到空格不停,遇到换行(回车)才停
- 关闭资源(用完了)
//1.import导入Scanner类
import java.util.Scanner;
public class SystemInDemo {
public static void main (String[] args) {
//2.实例化Scanner对象
Scanner input = new Scanner(System.in);//input变量代表了Scanner对象
//3.调用方法读取键盘输入的数据
System.out.print("请输入一个整数:");
int num = input.nextInt();//nextXxx方法返回读取到的数据,nextXxx方法都是阻塞函数,会停住等待键盘输入
//输出
System.out.println("读取到的数据是:" + num);
System.out.println("==================================");
System.out.print("请输入姓名和年龄(空格隔开):");
String name = input.next();
int age = input.nextInt();
//输出
System.out.println("我叫:" + name + ",年龄" + age);
//4.关闭资源
input.close();
}
}
注意:
- 键盘输入数据结束时,必须回车(\n换行),因为遇到换行才会刷新输入缓冲器,才会读取数据
- 输入数据的类型匹配问题:
- 可以读取数据之前,System.out输出一个提示信息(治标不治本)
- 在读取之前判断是不是需要的类型(预防)
- 类型错误以后,再处理错误
- 键盘输入数据时,如果一次输入多个数据,那么数据之间默认空格隔开(分隔符可以修改)
- 如果输入字符串时,字符串中就包含空格符,解决办法:修改分隔符。或者使用nextLine.
1.2 系统输出System.out
系统输出用来把数据输出到console控制台.
System.out的主要输出方法:
- println 输出最后会自动加上换行
- print 最后没有换行
- printf 格式化输出
- %s 字符串
- %d 十进制整数
- %o 八进制整数
- %x 十六进制整数
- %g 十进制浮点数
- %04d : 4代表输出的数据占据几位。不够默认补空格。0代表不够补0
- %.4f : .4代表小数点后边保留4位
public class SystemOutDemo {
public static void main (String[] args) {
//系统输出
System.out.print("这是一行\n");
System.out.println("这是下一行");
String name = "张飞";
int age = 20;
System.out.printf("姓名:%s,年龄:%d\n",name,age);//格式化输出,第一个参数定义输出格式,后边的参数是给格式中的占位符填充数据
int num1 = 15;
double num2 = 3.1415926;
int num3 = 0xf;
int num4 = 017;
int num5 = 0b1111;
System.out.printf("%04d\n",num1);//0015
System.out.printf("%o\n",num1);//17
System.out.printf("%x\n",num1);//f
System.out.printf("%.2f\n",num2);//3.14
System.out.println(num3 + "," + num4 + "," + num5);//
}
}
注意:Java中整数字面值默认十进制,0x开头的是十六进制;0开头的是八进制;0b开头的是二进制。
2.选择结构
选择结构是把代码的执行分为两个或者多个支路,通过条件选择其中一个支路执行。所以选择结构也叫做分支结构。
Java中选择结构有两种: if switch
2.1 if…else…
if…else…是否代码的执行分为两条支路,条件为true,执行if后的支路;否则执行else后边的支路。
语法:
if(条件){
//if支路的代码块
}else{
//else支路的代码块
}
import java.util.Scanner;
public class IfElseDemo {
public static void main (String[] args) {
Scanner input = new Scanner(System.in);//input变量代表了Scanner对象
//从两个数中找出较大的数
System.out.print("请输入两个整数:");
int num1=input.nextInt(),num2=input.nextInt();
int max;//较大的
if(num1>num2){
max = num1;
}else{
max = num2;
}
/*
int max = num1;
if(num1<num2){
max = num2;
}*/
System.out.println(num1+"和"+num2+"中较大的数是:" + max);
System.out.println("=======================================");
//成绩如果为负值,当作0处理
System.out.print("键盘输入成绩:");
int score=input.nextInt();
if(score<0){
score = 0;
}
System.out.println("成绩是:" + score);
System.out.println("=======================================");
if(num1>0)
System.out.println("正数");
else
System.out.println("非正数");
}
}
注意:
-
条件结果必须是boolean类型
-
else支路的代码块如果是空的,else可以省略
-
如果if或者else中的代码块只有一行(或者是一个结构),那么大括号可以省略(不建议)
2.2 if…else…的嵌套
if或者else中还可以再嵌套if…else…
if(){
if(){
}else{
}
}else{
if(){
}else{
}
}
2.2.1 基本嵌套方式
import java.util.Scanner;
public class MaxDemo{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
//从三个数中找出最大的数
System.out.print("请输入三个整数:");
int a = input.nextInt(),b = input.nextInt(),c = input.nextInt();
//先看第一个数是否是最大的
if(a > b && a > c){//a是最大的
System.out.println(a + "是最大的");
}else{//a不是最大的
//再看b是否是最大的
if(b>c){
System.out.println(b + "是最大的");
}else{
System.out.println(c + "是最大的");
}
}
/**
if(a>b){
if(a>c){//a
}else{//c
}
}else{
if(b>c){//b
}else{//c
}
}**/
}
}
2.2.2 特殊嵌套结构(多条件if)
多条件if是一种特殊的嵌套结构,特殊的地方:
if(条件1){
//...
}else if(条件2){
//...
}else if(条件n){
//...
}else{
//...
}
- 只有else中嵌套了新的if…else…
- else中只嵌套了一个if…else…,再没有其它代码
- 这种情况可以简化嵌套结构
- 常用于同一种条件的多分支(多区间)的判断
import java.util.Scanner;
public class IfElseIfElseDemo{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
//从三个数中找出最大的数
System.out.print("请输入三个整数:");
int a = input.nextInt(),b = input.nextInt(),c = input.nextInt();
//先看第一个数是否是最大的
if(a > b && a > c){//a是最大的
System.out.println(a + "是最大的");
}else if(b>c){
System.out.println(b + "是最大的");
}else{
System.out.println(c + "是最大的");
}
}
}
2.3 switch…case
switch…case这种选择结构是一种特殊的多条件选择结构,特殊的地方:
-
多条件必须是同一个表达式等于不同的几个常量值的判断
if(a==1){ //代码块1 }else if(a==2){ //代码块2 }else if(a==3){ //代码块3 }else{ //代码块n }
简化为switch的语法结构
switch(a){ case 1: //代码1 break; case 2: //代码2 break; case 3: //代码3 break; default: //代码n break; }
执行流程:
如果switch后边的表达式a等于第一个case后的常量1,就执行代码块1; 如果switch后边的表达式a等于第二个case后的常量2,就执行代码块2; 如果switch后边的表达式a等于第三个case后的常量3,就执行代码块3; 如果switch后边的表达式a不等于任何一个case后的常量,就执行代码块n;
import java.util.Scanner; public class SwitchCaseDemo{ public static void main(String[] args){ Scanner input = new Scanner(System.in); //模拟判断方向,控制物体的移动 System.out.print("请输入方向:"); String direction = input.next(); final String DOWN = "s"; /* if("w".equals(direction)){ System.out.println("物体往上移动"); }else if("d".equals(direction)){ System.out.println("物体往右移动"); }else if("s".equals(direction)){ System.out.println("物体往下移动"); }else if("a".equals(direction)){ System.out.println("物体往左移动"); }else{ System.out.println("物体不移动"); }*/ switch(direction){ case "w": System.out.println("物体往上移动"); break; case "d": System.out.println("物体往右移动"); break; case DOWN://final常量也可以 System.out.println("物体往下移动"); break; case "a": System.out.println("物体往左移动"); break; //case 后边的值不能重复 /** case "a": System.out.println("物体往左移动"); break;**/ default: System.out.println("物体不移动"); break; } //switch后边只支持int、String和枚举 /** double d = 3.15; switch(d){ case 1.5: break; }*/ } }
注意:
-
switch后边的表达式的结果一定是int(byte、short、char)或者String或者枚举
否则报错:
SwitchCaseDemo.java:46: 错误: 不兼容的类型: 从double转换到int可能会有损失 switch(d){ ^ 1 个错误
-
case后边必须是常量: 字面值常量 或者 final修饰的常量
否则报错:
SwitchCaseDemo.java:28: 错误: 需要常量字符串表达式 case down: ^ 1 个错误
-
case后边的常量不能重复
否则报错:
SwitchCaseDemo.java:34: 错误: case 标签重复 case "a": ^ 1 个错误
-
正常情况下,每个case(包括default)的代码块最后都应该加上break;语句。break可以实现case之间的互斥。
-
2.4 break;
break;语句的作用是结束结束当前结构的执行,也就是结束switch…case结构的执行。
注意:
-
如果switch…case中每个case后边都不加break语句,那么当匹配上某个case以后,就从这个case中的语句开始执行,一直执行到switch…case结束。
只有遇到break;才会结束。
-
有极其特殊的情况下,才有可能某些case后边不加break;实现case后边代码的复用
import java.util.Scanner;
public class BreakDemo{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
//判断象棋某个棋子的移动规则
System.out.print("输入棋子标记:");
String item = input.next();
switch(item){
case "帅":
case "将":
System.out.println("只能在中间的4格中移动");
break;
case "兵":
case "卒":
System.out.println("只能往前,不能后退");
break;
case "象":
case "相":
System.out.println("只能走田,不能过河");
break;
case "士":
System.out.println("只能在中间的4格中斜着移动");
break;
case "炮":
System.out.println("只能直线运动,并且隔着一个子吃掉对方的子");
break;
case "车":
System.out.println("只能直线运动");
break;
case "马":
System.out.println("只能走日");
break;
default:
System.out.println("无效标记");
break;
}
}
}
说明: 一个case中如果没有break语句,那么就可以包含执行下一个case中的代码。
五、循环结构
循环结构是控制某个代码块循环多次执行。只要条件还为true,就继续执行一次;一旦条件为false,就结束循环。
Java中有三种循环结构: while , do…while , for
1.while
语法:
while(条件){
//要循环多次执行的代码块
}
执行流程:
-
a.判断条件
-
b.条件为true,执行一次代码块;条件false,结束执行。
-
c.循环a,b的过程
条件true --> 执行一次代码块—> 条件true --> 执行一次代码块) —> … —> 条件flase --> 结束
基本循环结构的四要素
- 声明循环变量:每个循环中一般都有一个循环在变化的值,用来控制循环条件的真假变化(其实就是用来控制循环次数)。
- 循环条件:一般循环条件是个表达式,这个表达式中包含有循环变量
- 要循环多次执行的代码块
- 循环变量的变化
public class WhileDemo{
public static void main(String[] args){
//循环输出1到100
//把输出一个整数的语句循环执行100次
int i = 1;//声明循环变量(计数器)
while(i<=100){//循环条件
System.out.print(i + " ");//循环执行的代码块
++i;//循环变量的变化
}
}
}
注意:
- 循环条件的结果必须boolean类型
- 循环变量的变化不要忘了(++i),不然死循环
2.do…while
2.1 语法:
do{
//循环执行多次的代码块
}while(条件);
执行流程:
-
a.先执行一次代码块
-
b.然后判断条件
-
c.条件是true,执行一次代码块;条件是false,退出循环
-
d.循环b,c的过程
执行一次代码块—> 条件true --> 执行一次代码块) —> … —> 条件flase --> 结束
2.2 do…while和while的区别:
- do…while先执行一次循环体,才开始判断条件。循环体至少执行一次.
- while先判断条件,决定是否执行代码块。while有可能循环体一次都不执行.
使用场景:
- 如果一个循环问题,他的代码块至少执行一次,就选择do…while
- 如果一个循环问题,他的代码块有可能一次都不执行,就选择while
import java.util.Scanner;
public class DoWhileDemo{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
//计算n的阶乘
int n = 5;
int result = 1;//初始值1
int i = 1;//声明循环变量
do{
result = result * i;//循环执行的代码块
++i;//循环变量的变化
}while(i<=n);//循环条件
System.out.println(n+"的阶乘是:" + result);
System.out.println("====================");
//模拟玩游戏
String is;
do{
System.out.println("玩一局游戏");
System.out.print("是否继续(y/n):");
is = input.next();
}while("y".equals(is));
System.out.println("结束退出");
}
}
注意:
- do…while中while条件小括号后边的分号不能省略。
2.3 Random
Random可以用来生成随机数.
使用步骤类似Scanner:
- import导入Random
- 实例化Random对象
- 调用nextXxx方法,生成对应类型的随机数
import java.util.Scanner;
import java.util.Random;
public class GuessDemo{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
Random rand = new Random(System.currentTimeMillis());//以系统时间作为随机种子
//模拟数字炸弹
//生成一个随机数,作为目标数字
int num = rand.nextInt(100);//[0,100)
//循环猜测
int min = 0;//下限
int max = 100;//上限
int guess;
do{
System.out.print(min + "到" + max + ":");
guess = input.nextInt();//猜
if(guess > num){//太大了,当前数作为下一次的上限
max = guess;
}else if(guess < num){//太小了,当前数作为下一次的下限
min = guess;
}
}while(guess != num);//没猜对,继续循环
System.out.println("猜对了,就是"+num);
}
}
变量作用域问题:
-
局部变量的作用域是:包围这个变量声明语句的最内层的大括号。
作用域:就是只能在这个范围中使用。
3.for
for循环是对while循环结构的简化, 不管是执行流程,还是使用场景都跟while类似。
循环结构的基本四要素:
- 声明循环变量
- 循环条件
- 循环执行的代码块
- 循环变量的变化
语法:
for(1;2;4){
3
}
一般情况下:
- 1的位置是声明循环变量
- 2的位置是循环条件
- 3的位置是循环执行的代码块
- 4的位置是循环变量的变化
执行流程:
- a.先执行位置1的代码(只执行一次),一般是循环变量的声明或者初始化
- b.然后执行位置2的循环条件
- c.如果条件是true,先执行位置3的代码块,紧跟着执行位置4的代码。(位置4一般是循环变量的变化 ,也就是赋值语句,不能是System.out之类的语句)
- 循环b,c的过程
import java.util.Scanner;
public class ForDemo{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
//计算n的阶乘
System.out.print("请输入一个正整数:");
int n = input.nextInt();
// 个位 : n/1%10 十位: n/10%10 百位: n/100%10 千位: n/1000%10
for(int i=1;n/i!=0;i*=10){
int wei = n/i%10;
System.out.println(wei);
}
}
}
注意:
- for循环的四部分都可以为空,但是小括号中的两个分号不能少。
- 位置1为空,也就是可以把循环变量的声明放到for的外边,像while一样
- 位置4为空,也就是把循环变量的变化又重新放回到循环体的最后一句。
- 位置2为空,也就是循环条件为空,代表循环条件为永真。会造成死循环。
- 最简单的死循环
import java.util.Scanner;
public class ForDemo2{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
//输出1到100之间所有的偶数
int i=1;//应该放在位置1的代码
for(;i<=100;){
if(i%2==0){
System.out.print(i + " ");
}
++i;//应该放在位置4的代码
}
System.out.print("\n========================\n");
for(;;);//这是死循环(最简单的for死循环)
//while(true);
//do{}while(true);=
}
}
-
关于for中位置1声明的变量的作用域问题
位置1声明的变量只能在for结构中使用.
public class ForDemo3{
public static void main(String[] args){
//找出0到100以内能被12整除的最大整数
int j;
for(j=100;j>0&&j%12!=0;j--);//for循环小括号中声明的变量,作用域仅限于for结构中
System.out.println("结果是" + j);
}
}
4.break
break语句的作用是结束当前结构(包括循环结构和switch…case选择结构)。
语法:
break;
//或者
break 标签名;
break;单独使用表示结束当前结构的执行;break加上标签表示结束指定的结构的执行。
使用场景:
- 有时在执行到某一次迭代的时候,已经达到目的或者已经得到结果,这时循环没必要再继续执行了,就可以使用break结束循环。
import java.util.Scanner;
public class BreakDemo2{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
//从键盘输入一个正整数,判断是否是质数(素数)
//不能被2到n-1(其实只需要到n的平方根即可)整除
System.out.print("输入一个正整数:");
int n = input.nextInt();
boolean is = true;//结果,默认是质数
for(int i=2;i<=Math.sqrt(n);++i){
if(n%i==0){//已经得到结果,不是质数
is = false;
break;
}
}
if(is){
System.out.println(n + "是质数");
}else{
System.out.println(n + "不是质数");
}
}
}
- 有时循环正常的循环条件不好找,但是循环退出的条件比较明显,这时可以把循环条件写为true死循环,然后在需要退出循环时使用break;
import java.util.Scanner;
public class BreakDemo3{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
//从键盘循环输入多个成绩,计算平均成绩。如果输入-1代表结束
int score;
int total = 0;
int count = 0;
while(true){//死循环
System.out.print("请输入成绩:");
score = input.nextInt();
if(score == -1){//-1的时候,break结束循环
break;
}
total += score;
count++;
}
System.out.println("总成绩:" + total + ",总个数:" + count + ",平均成绩:" + total*1.0/count);
}
}
5.continue
continue并不结束循环的执行。continue是跳过当次迭代后边剩余的语句,直接又去判断条件,开始下一次迭代。
while(条件){
//(1)
if(条件){
continue;
}
//(2)
}
continue的作用: 就是当执行到continue的时候,会跳过continue后边位置(2)的代码,直接再去判断循环条件,做下一次迭代。
import java.util.Scanner;
public class ContinueDemo{
public static void main(String[] args){
//输出1到100之间的奇数
for(int i=1;i<=100;i++){
if(i%2==0){//如果是偶数,跳过输出.继续下一个
continue;
}
System.out.print(i + " ");
}
System.out.print("\n========================\n");
for(int i=1;i<=100;i++){
if(i%2!=0){
System.out.print(i + " ");
}
}
}
}
- 很多时候,只要稍作逻辑的变化,既可以避免使用continue
6.Math
Math工具类中包含了大量的数学运算的工具方法:
- Math.sqrt(n) 平方根
- Math.pow(x,y) x的y次方
- Math.abs(n) n的绝对值
- …
7.循环嵌套
一个循环中再嵌套另一个循环。也就是循环中的循环体也需要循环去处理。
以双重循环为例:
while(){
//
while(){
}
//
}
public class MultiLoopDemo{
public static void main(String[] args){
//计算1! + 2! + 3! + 4! + ... + 10!
int sum = 0;
for(int i=1;i<=10;i++){
//计算i的阶乘
int jie = 1;
for(int j=1;j<=i;j++){
jie *= j;
}
sum += jie;//求和
}
System.out.println("1到10的阶乘之和是:" + sum);
}
}
双重循环经常可以用来输出二维图形: 直角三角形、九九乘法表
1*1=1
1*2=2 2*2=4
......
1*9=9 2*9=18 ...... 9*9=81
for(int i=1;i<=9;i++){
for(int j=1;j<=i;j++){
System.out.print(j + "*" + i + "=" + (j*i) + "\t");
}
System.out.println();
}
关于break;或者continue;在循环嵌套中的使用:
-
break 加上标签名可以直接退出指定循环的执行。
import java.util.Scanner; public class MultiLoopDemo3{ public static void main(String[] args){ Scanner input = new Scanner(System.in); /** 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 **/ //外层控制 5行 label1:for(int i=1;i<=5;i++){ //打印一行 label2:for(int j=1;j<=10;j++){ System.out.print(j + " "); if(i%4 == 0 && j%8 == 0){ break label1;//结束label1指示的循环 } } System.out.println(); } } }
六、数组
数组可以用来存储一组同类型的数据。
一个数组代表的是内存中连续的一串内存空间,所以可以存储多个同类型数据。
概念:
- 数组的元素:数组中存储的每个数据叫做数组的一个元素。
1.创建数组
创建数组就是分配内存空间。内存空间由元素的类型和个数决定。所以创建一个数组,核心的两个要素就是元素类型和元素个数。
语法:
new 元素类型[元素个数];
new int[7];
说明: 数组的长度(也就是元素个数)一旦确定,不能修改。也就是数组长度不可变。
2.引用数组
要使用数组,必须先声明一个数组类型的变量,引用到这个数组才可以。
通过引用了数组的这个变量,就可以访问到数组。
数组类型的语法:
元素类型[]
int[]
声明数组类型变量的语法:
元素类型[] 变量名;
正常情况下,先声明变量,再创建数组,然后赋值(就是让变量引用数组)。
比如:
int[] arr;
arr = new int[7];
等价于
int[] arr = new int[7];
3.访问数组
访问数组主要是访问数组中的元素。
访问数组中的元素通过元素的下标访问,下标从0开始,最后一个元素的下标是数组长度-1。
语法:
数组名[下标]
arr[0]
arr[6]
每个元素就相当于一个普通变量,无非就是赋值和取值。
经常也需要访问数组的长度,数组提供了一个属性length代表长度:
数组名.length
arr.length
public class ArrayDemo{
public static void main(String[] args){
//1.创建数组,并声明数组变量引用之
int[] arr = new int[5];
//2.访问数组中的元素
arr[0] = 10;
arr[1] = 11;
arr[2] = 12;
arr[3] = 13;
arr[4] = 14;
System.out.println(arr[0] + "," + arr[1] + ","
+ arr[2] + "," + arr[3] + "," + arr[4]);
//3.如果要依次对数组中的每个元素做同样的操作,叫做遍历数组.使用循环
for(int i=0;i<=arr.length-1;i++){
System.out.print(arr[i]);
if(i<arr.length-1){
System.out.print(",");
}
}
}
}
下标如果超出0到length-1的范围,会报错:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
at ArrayDemo.main(ArrayDemo.java:7)
4.数组中元素的初始化问题
数组中的元素在创建数组的时候就自动初始化好了。
- 整形(包括char)元素自动初始化为0
- 浮点型元素自动初始化为0.0
- 布尔类型元素自动初始化为false
- 引用数据类型(String)自动初始化为null
也可以自己在创建数组的时候指定每个元素的初始值。支持两种语法:
-
在创建数组的同时给出所有初始值.
new 元素类型[]{元素1,元素2,元素3,...,元素n};
new int[]{11,12,13,14,15};
注意:方括号中一定不能写长度。长度是根据给的初始值的个数确定的.
-
在声明数组变量的同时创建数组,并给出每个元素的初始值
元素类型[] 变量 = {元素1,元素2,元素3,...,元素n};
int[] arr = {11,12,13,14,15};
public class ArrayInitDemo{
public static void main(String[] args){
//1.创建数组,并声明数组变量引用之
int[] arr = new int[5];
//整形数组默认初始化为全0
for(int i=0;i<=arr.length-1;i++){
System.out.print(arr[i]);
if(i<arr.length-1){
System.out.print(",");
}
}
System.out.print("\n===========================\n");
//在创建数组的时候,给出每个元素的初始值
int[] arr2 = new int[]{10,11,12,13,14};
arr2 = new int[]{20,21,22,23,24};
for(int i=0;i<=arr2.length-1;i++){
System.out.print(arr2[i]);
if(i<arr2.length-1){
System.out.print(",");
}
}
System.out.print("\n===========================\n");
//在声明数组变量的同时创建数组并给出每个元素的初始值
int[] arr3 = {100,110,120,130,140};
//arr3 = {100,210,220,230,240}; 没有在声明同时,所以不允许
for(int i=0;i<=arr3.length-1;i++){
System.out.print(arr3[i]);
if(i<arr3.length-1){
System.out.print(",");
}
}
}
}
5.声明数组变量的语法问题
声明数组变量时,方括号可以写在变量名的前边;也可以写在变量名的后边。
元素类型[] 变量名;
元素类型 变量名[];
int[] arr;
int arr2[];
练习: 键盘录入5个学生成绩,存储到数组中。计算平均成绩。
练习:直接初始化一个数组{13,24,56,-1,35,100},统计数组中有多少个负数。
6.ForEach循环
ForEach循环是for循环的一种变形,并且只能用于对数组(集合、列表)进行遍历。
语法:
for(元素类型 变量名 : 数组){
//
}
int[] arr = {1,3,6,10};
for(int a : arr){
System.out.println(a);
}
这种ForEach循环,每次迭代会自动取出数组中的元素,赋值给变量a。
这种ForEach循环有缺陷,不能对元素做修改操作。
7.查找算法和排序算法
7.1 查找算法
从数组中找出某个目标数所在的位置。
-
遍历
遍历数组,一个一个比较。
import java.util.Scanner; public class SearchDemo{ public static void main(String[] args){ Scanner input = new Scanner(System.in); int[] arr = {23,45,1,10,100,50}; System.out.print("请输入一个整数:"); int key = input.nextInt(); //遍历查找,如果找打了,结果是下标。如果没找到,就输出没找到 int index = -1;//-1代表没找到 for(int i=0;i<=arr.length-1;i++){ if(arr[i] == key){//找到了 index = i; break; } } if(index >= 0){ System.out.println("找到了,下标是:" + index); }else{ System.out.println("没找到"); } } }
-
二分
二分是对有序的数组进行查找。
7.2 排序算法
排序就是对数组中元素进行升序或者降序的排列。
-
选择排序
练习: 从一个数组中找出最大值
public class SelectSortDemo{ public static void main(String[] args){ int[] arr = {12,57,2,6,100,88}; /* //找出最大值 int max = arr[0];//初始化下标0位max最大值 //从下标1开始,以此跟max比较,大的放在max中 for(int i=1;i<=arr.length-1;i++){ if(arr[i] > max){ max = arr[i]; } } System.out.println("最大值是:" + max); */ //选择排序,以降序为例(从大到小) //0. 从数组(0到length-1)找出最大值,放到0下标 //1. 从数组(1到length-1)找出最大值,放到1下标 //2. 从数组(2到length-1)找出最大值,放到2下标 //。。。。。。 //length-2. 从数组(length-2到length-1)找出最大值,放到length-2下标 for(int i=0;i<=arr.length-2;i++ ){ for(int j=i+1;j<=arr.length-1;j++){ if(arr[j] > arr[i]){//大的留在擂台,小的回到底下位置 //交换 int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } } } for(int i=0;i<=arr.length-1;i++){ System.out.print(arr[i] + " "); } } }
-
冒泡排序
-
插入排序
-
快速排序
8.Arrays
Arrays工具类中提供了大量的处理数组的方法,包括复制数组,二分查找,快速排序等等.
-
Arrays.sort()
-
Arrays.toString()
-
等等
9.多维数组
多维数组 就是 数组总存储的元素还是一个一个数组。 也就是元素类型也是数组类型的数组,称为多维数组。
严格意义上说,Java中其实只有数组,没有多维数组。因为Java的多维数组其实就是元素类型比较特殊而已。
以二位数组为例讲解。
9.1创建二位数组
语法:
创建二位数组的语法:第一个方括号中写二维数组的长度。
int[][] arr = new int[5][];
创建好二位数组以后,一定要创建包含的每个一维数组。
arr[0] = new int[4];
arr[1] = new int[3];
arr[2] = new int[5];
arr[3] = new int[2];
arr[4] = new int[1];
9.2访问二维数组
访问二位数组,主要访问的是二维数组中的一维数组中的元素。
arr[二维的下标][一维的下标]
arr[0][0]
arr[1][2]
public class BinaryArrayDemo{
public static void main(String[] args){
//创建二维数组
int[][] arr = new int[3][];
arr[0] = new int[3];
arr[1] = new int[2];
arr[2] = new int[1];
//访问二维数组
arr[0][0] = 3;
arr[0][1] = 2;
arr[0][2] = 1;
arr[1][0] = 2;
arr[1][1] = 1;
arr[2][0] = 1;
//遍历
//外层循环遍历二维数组
for(int i=0;i<=arr.length-1;i++){
//内层循环遍历一维数组
for(int j=0;j<=arr[i].length-1;j++){
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
}
9.3声明创建的同时初始化
二维数组也支持在创建或者声明数组的同时,给数组元素进行初始化。而且支持二维和一维同时初始化。
public class BinaryArrayInitDemo{
public static void main(String[] args){
//创建二维数组的同时,给二维数组初始化
int[][] arr = new int[][]{new int[3], new int[2], new int[1]};
//创建二维数组的同时,给二维数组和一维数组都初始化
int[][] arr2 = new int[][]{new int[]{3,2,1}, new int[]{2,1}, new int[]{1}};
//更简单的语法(必须在声明数组变量的同时)
int[][] arr3 = {new int[3], new int[2], new int[1]};
int[][] arr4 = {
{3,2,1},
{2,1},
{1}
};
for(int i=0;i<=arr4.length-1;i++){
for(int j=0;j<=arr4[i].length-1;j++){
System.out.print(arr4[i][j] + " ");
}
System.out.println();
}
System.out.println("============================");
//ForEach遍历二维数组
for(int[] a : arr4){
for(int b : a){
System.out.print(b + " ");
}
System.out.println();
}
}
}
9.4声明数组变量的语法问题
声明数组变量时,方括号可以在变量名前边或者后边。
int[][] arr;
int arr[][];
int[] arr[];
练习: 杨辉三角形
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
。。。。。。。。。。
二维数组存储杨辉三角形
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
............
9.5二维数组的语法糖
如果二维数组中包含的所有一维数组的长度是一样的,这时可以简化语法,在创建二维数组的同时就可以创建好所有一维数组。
int[][] arr = new int[二维数组的长度][一维数组的长度];
int[][] arr = new int[3][5];
七、方法
1.概念
方法是对一个功能单元的封装。也就是方法把一个功能模块的代码提取出来,封装起来,需要使用这个功能时,直接调用该方法即可。
方法的优点:
- 有利于代码复用。功能代码只需要编写一次,可以重复多次调用.
- 方法的封装可以隐藏功能的具体实现方式。调用者只需要知道方法的功能和调用方式即可,不需要知道功能如何实现的。
- 有利于简单的模块化。可以把复杂功能逻辑拆分为多个简单的功能单元,以便实现和后期的维护。
2.方法的定义
定义方法就是声明方法的原型,并实现方法的功能。
语法:
public static 返回结果的数据类型 方法名 (参数列表) {
//可执行的代码块,也叫作方法体。就是实现方法的功能
}
public static int add (int a, int b) {
return a+b;
}
说明:
-
public static 不是必须的,但是现阶段必须写上。后续讲解。
-
返回结果的数据类型:这里需要一个数据类型,也简称为返回类型。
就是指定方法最后返回的结果数据的类型。如果没有返回结果,写一个void。
-
方法名:跟变量名一样,都是标识符,一般建议方法名首字母小写.
-
参数列表:参数列表是定义调用时要传递给方法的数据的类型和个数。可以为空。
-
方法体:就是可执行的代码块,用来实现方法的功能。
-
一般把方法体,也就是大括号中的内容成为方法的实现。把大括号之前的内容成为方法的原型。
3.方法的调用
方法定义好以后,就可以在需要的地方调用它。注意:方法不调用,就不执行。
语法:
方法名(参数列表)
add(10,20);
注意:
- 如果没有参数,参数列表可以为空。小括号不能省略。
4.方法的参数
方法的参数也叫作输入数据。也就是在方法中要使用,但是由调用者提供的数据。即把外部(调用处)的数据传递到方法中。
参数分为形式参数和实际参数。
4.1 形式参数(形参)
把定义方法时参数列表中的参数,叫做形式参数。
形式参数只是告诉调用者需要传递几个什么类型的数据。
语法:
(数据类型 参数名1, 数据类型 参数名2, ... ,数据类型 参数名n)
(int a, String s, double d)
- 形参的声明和变量的声明语法是一样的,并且形参在方法中的使用就是当做变量使用。
- 数据类型可以是基本数据类型,也可以是引用数据类型
- 参数名和变量名一样,都是标识符,建议首字母小写
- 多个参数逗号隔开
4.2 实际参数(实参)
把调用方法时参数列表中的参数,叫做实际参数。
实际参数就是调用者要传递给方法的数据。
语法:
(数据1,数据2, ... , 数据n)
(10, "张三", 178.5)
- 实参是按照形参指定的类型和顺序给的数据,所以实参要和形参一一匹配(类型和个数)。
- 参数之间逗号隔开
5.方法的返回值
方法的返回值是方法功能实现的结果数据,要返回给调用者.
5.1 定义方法时
-
要指定返回结果的数据类型
-
在方法体中得到结果以后,必须使用return语句返回结果数据.
return 结果数据;
-
如果方法没有返回结果,那么返回类型就是void,然后方法体中也可以使用return语句,但是不能带结果数据。
return;
5.2 调用方法时
如果有返回结果,需要接收返回的结果数据.直接把返回结果存储到变量中即可。
类型 变量 = 方法调用;
int sum = add(10,20);
6.方法调用时代码的执行流程
当代码执行到方法调用语句时:
- 首先调转到方法定义的地方
- 然后把实参一一赋值给形参,就是用实参给形参初始化
- 接下来执行方法体中的代码
- 直到遇到return语句或者方法体中代码执行完毕,会返回到方法调用的地方。如果有返回值,也会把返回值带回去。
import java.util.Scanner;
import java.util.Arrays;
public class MethodDemo{
public static void main (String[] args){
//请打印1到10
//调用方法
printNumbers();
System.out.println("===================");
//请再次打印1到10
printNumbers();
System.out.println("===================");
//请打印所有的水仙花数
printAllShuiXianHua();
System.out.println("===================");
Scanner input = new Scanner(System.in);
System.out.print("输入一个整数:");
int num = input.nextInt();
//计算num的绝对值
int r = abs(num);//实参
System.out.println(num + "的绝对值是:" + r);
System.out.println("===================");
System.out.print("输入两个整数:");
int num1 = input.nextInt();
int num2 = input.nextInt();
int m = max(num1,num2);
System.out.println(num1 + "," + num2 + "中较大的是:" + m);
System.out.println("===================");
int[] array = {3,18,1,20,100,50};
int max = maxArray(array);
System.out.println(Arrays.toString(array) + "中最大值是:" + max);
System.out.print("输入一个整数:");
int key = input.nextInt();
int index = searchArray(array, key);
if(index >= 0)
System.out.println(Arrays.toString(array) + "中"+key+"的位置是:" + index);
else
System.out.println("没找到");
}
/**
* 控制台输出1到10
**/
public static void printNumbers(){
for(int i=0;i<10;i++){
System.out.print((i+1) + " ");
}
System.out.println();
}
/**
* 打印所有的水仙花数
*/
public static void printAllShuiXianHua(){
int ge,shi,bai;
for(int i=100;i<=999;i++){
ge = i/1%10;
shi = i/10%10;
bai = i/100%10;
if(ge*ge*ge + shi*shi*shi + bai*bai*bai == i){
System.out.print(i + "\t");
}
}
System.out.println();
}
/**
* 计算一个整数的绝对值
* @param a 指定的整数
* return 返回a的绝对值
*/
public static int abs(int a){//形参
int result;
//a的绝对值
if(a>=0){
result = a;
}else{
result = 0-a;
}
return result;//返回结果
}
/**
* 从两个整数中找出较大的数
* @param a 整数1
* @param b 整数2
* return 返回a,b中较大的
*/
public static int max(int a, int b) {
if(a>=b){
return a;
}else{
return b;
}
}
/**
* 从数组中查找最大值
* @param arr 指定数组
* return 返回最大的值
*/
public static int maxArray(int[] arr){
if(arr == null || arr.length <= 0){
return -100;//应该抛出异常
}
if(arr.length == 1){
return arr[0];
}
int max = arr[0];
for(int i=1;i<=arr.length-1;i++){
if(arr[i] > max){
max = arr[i];
}
}
return max;
}
/**
* 从数组中查找数据
* @param arr 指定数组
* @param key 要找的目标数
* return 返回目标数在数组中的下标位置。找不到返回-1
*/
public static int searchArray(int[] arr, int key){
if(arr==null || arr.length<=0){
return -1;
}
for(int i=0;i<=arr.length-1;i++){
if(arr[i] == key){
return i;
}
}
return -1;
}
}
7.方法重载
重载(overload):描述的是多个方法之间的一种关系。
满足以下条件的方法可以构成重载关系:
- 方法名形同。(同一个类中)
- 形参列表不同。不同包括参数类型不同,或者参数个数不同。
注意:
- 重载与方法的参数名无关,与返回值类型无关,与方法的其他修饰词也无关。
简单来说:重载其实就是同一种功能,针对不同的参数做的不同的实现。
比如:
public static int add(int a ,int b){}
public static double add(double a, double b){}
如果有重载,方法调用时:用实参和形参的匹配来选择调用哪个方法。
- 优先当然是完全匹配
- 有一种不完全匹配也可以调用,就是实参可以自动类型转换为形参。
像System.out中的println方法就重载了好多个。
- println(int)
- println(double)
- println(String)
八、面向对象
1.类和对象
类是一种类型(类别),是为了给对象进行分类的。分类更有益于对象的管理,以及同一种对象的特征和行为的定义。因为同一种对象的特征和行为是一样的,所以直接按照类别去定义特征和行为最方便。
结论: 先应该定义类,在按照类中定义的特征和行为创建出对应类型的对象.
概念:
-
类:类是对某一种类型的对象所共有的特征(属性/状态)和行为的抽象和描述。
一般也把类称为对象的模板。比如: 人类、动物类、计算机类 …
-
对象:对象是类的实例化,就是类代表的类别一个具体的实例。
对象就是按照类这种模板造出来的实物。比如:张三这个人、tom这只猫、编号是1001这台计算机 。。。。
1.1类的定义
类的定义主要是类名,以及类中包含的成员。成员主要就是这种类别的对象所共有的属性和行为的描述。
-
属性对应到代码中就是变量,这种变量称为成员变量.
属性是静态的数据,所以用变量就可以存储。
-
行为对应到代码中就是方法,也可以称为成员方法。
行为是动态的功能,所以用方法去实现。
-
类中还可以包括代码块
-
类中还可以包含内部类(接口、枚举、注解)
语法:
[修饰词] class 类名 {
//类中的成员
//1.成员变量
//2.方法
//3.代码块
//4.内部类
}
/**
* 学生类
*/
public class Student {
//类中的成员
//1.成员变量(属性)
long studentNo;//学号
String studentName;//姓名
String studentGender;//性别
String studentClass;//班级
int studentAge;//年龄
//2.方法(行为)
/**
* 打招呼
* @param other 另一个学生
*/
public void sayHi(String other){
System.out.println("你好:" + other);
}
/**
* 学习
* @param course 指定课程
*/
public void learn(String course){
System.out.println("今天学习了:" + course);
}
//3.代码块
{
//多行可执行的代码
}
//4.内部类
class StudentInner{}
}
注意:
- 类名是标识符,建议首字母大写
- 所有的方法都先不要加static关键字
- 代码块和内部类后续讲解
1.2实例化对象
类定义好以后,就可以按照类(模板)去实例化一个对象,那么对象中就包含了类中给定义的成员。
实例化对象的语法:
new 类名();
new Student();
一般实例化出来的对象需要用同类型的变量引用之,以便继续使用这个对象。
Student stu = new Student();
-
所以每个类都是一种新的数据类型,也称为自定义的数据类型。凡是需要数据类型的地方,都可以使用类名。类都是引用数据类型。
比如:变量类型 、 参数类型、返回值类型、数组的元素类型、成员变量的类型
1.3访问对象中的成员
对象中的成员变量、方法、内部类都可以访问.
对象中各种成员的访问语法是一样的,都是通过对象.去访问.
-
成员变量
对象.成员变量名
stu.studentNo stu.studentName
-
方法
方法也都是通过对象.去调用
对象.方法名([实参]);
stu.sayHi("张三"); stu.learn("Java程序设计");
public class Application{
public static void main(String[] args){
//实例化一个Student对象,并声明一个Student类型的变量去引用这个对象
Student stu = new Student();
//对象的使用无非就是访问对象中的成员(代码块这种成员不能访问)
//1.访问对象中的成员变量
stu.studentNo = 1001;
stu.studentName = "张小马";
stu.studentGender = "男";
stu.studentClass = "软件1班";
stu.studentAge = 18;
System.out.println(stu.studentNo + "," + stu.studentName + "," + stu.studentGender + ","
+ stu.studentClass + "," + stu.studentAge);
//2.调用对象中的方法
stu.sayHi("王小六");
stu.learn("Java程序设计");
//3.内部类
//stu.new StudentInner();
System.out.println("=======================");
Student stu2 = new Student();
stu2.sayHi("李想");
stu2.learn("Java高级API");
}
}
1.3this
this代表一个对象:当前对象。
如果要在一个成员中,访问当前对象中的另一个成员,就需要使用this.
/**
* 图书类
*/
public class Book{
//成员变量
long bookId;
String bookName;
String bookAuthor;
String bookPress;
int bookStatus;
/**
* 打印当前图书对象的信息
*/
public void printInfo(){
//this代表当前对象,可以用来访问当前对象中的其他成员
System.out.println("*******************");
System.out.println("图书编号:" + this.bookId);
System.out.println("图书名称:" + this.bookName);
System.out.println("图书作者:" + this.bookAuthor);
System.out.println("图书出版社:" + this.bookPress);
System.out.println("图书状态:" + this.bookStatus);
System.out.println("*******************");
}
}
-
如果实在一个方法中使用this,那么 当前对象就是正在调用这个方法的对象。
-
注意:this.可以省略,但是不建议省略。
this的特殊使用情况:一个构造方法中调用另一个构造方法,使用this,并且这个语句必须是第一条语句。
this(参数);
//构造方法
public Student(){
//在一个构造中是可以调用另一个构造
this(10000,"张飞","男","软件2班",25);//this代表要调用另一个构造,小括号中是实参
System.out.println("这是Student的构造方法()");
/*
this.studentNo = 10000;
this.studentName = "张飞";
this.studentGender = "男";
this.studentClass = "软件2班";
this.studentAge = 25;*/
}
public Student(long studentNo, String studentName,String studentGender,String studentClass,int studentAge){
System.out.println("这是Student的构造方法(long,String,String,String,int)");
this.studentNo = studentNo;
this.studentName = studentName;
this.studentGender = studentGender;
this.studentClass = studentClass;
this.studentAge = studentAge;
}
2.对象的构造过程
2.1 构造过程
当使用new实例化一个对象时,底层虚拟机做了一下操作:
- 第一:new代表分配内存空间。主要给对象的所有成员变量分配空间。
- 第二:初始化成员变量
- 第三:执行一次代码块
- 第四:调用执行一次构造方法
2.2 构造方法
一种特殊的方法,特殊在两个地方:
-
定义的语法特殊
- 不能有返回类型
- 方法 名必须就是类名
public 类名(形参列表){ //方法体 }
public Student(){ }
-
调用特殊
- 只能在new对象的时候自动调用,不能在其他地方调用。
- 构造方法专门是用来构造对象的,一般里边的代码是做对象的初始化工作。
new Student();
更新下创建对象的语法:
new 构造方法调用;
new Student(); new Student(实参);
一个类中可以定义多个构造方法,这些构造方法一定构成重载关系。
注意:一个类中如果没有显式的定义构造方法,那么系统会给类自动提供一个无参的构造方法。但是,如果自己显式定义了构造方法,那么系统的这个无参的就消失了,就调用不到了。所以建议,如果自己定义构造,无参的一定加上。
/**
* 学生类
*/
public class Student {
//类中的成员
//1.成员变量(属性)
long studentNo = 100;//学号
String studentName = "张飞";//姓名
String studentGender = "男";//性别
String studentClass = "数据1班";//班级
int studentAge = 18;//年龄
//2.方法(行为)
//构造方法
public Student(){
System.out.println("这是Student的构造方法()");
this.studentNo = 10000;
this.studentName = "张飞";
this.studentGender = "男";
this.studentClass = "软件2班";
this.studentAge = 25;
}
public Student(long studentNo, String studentName,String studentGender,String studentClass,int studentAge){
System.out.println("这是Student的构造方法(long,String,String,String,int)");
this.studentNo = studentNo;
this.studentName = studentName;
this.studentGender = studentGender;
this.studentClass = studentClass;
this.studentAge = studentAge;
}
/**
* 打招呼
* @param other 另一个学生
*/
public void sayHi(String other){
System.out.println(this.studentName + "说:你好," + other);
}
/**
* 学习
* @param course 指定课程
*/
public void learn(String course){
System.out.println(this.studentName + "今天学习了:" + course);
}
public void show(){
System.out.println(this.studentNo + "," + this.studentName + "," + this.studentGender + ","
+ this.studentClass + "," + this.studentAge);
}
//3.代码块
{
//多行可执行的代码
System.out.println("这是Student中的特殊成员:代码块");
this.studentNo = 1000;
this.studentName = "张翼德";
this.studentGender = "男";
this.studentClass = "软件1班";
this.studentAge = 20;
}
}
public class App{
public static void main(String[] args){
Student stu = new Student();
stu.show();
System.out.println("=================");
Student stu2 = new Student(10001,"刘备","男","软件3班",40);
stu2.show();
}
}
2.3 对象的初始化
对象的初始化一般放在代码块中或者构造方法中。但是一般选择构造方法。
初始化工作中最简单的一种初始化就是给成员变量初始化。
-
声明的同时进行成员变量的初始化
这种只适合所有对象的成员变量的初始化值固定
-
代码块中初始化
代码块一般用来给对象进行初始化,所以一般也叫作 初始化块。
初始化块中进行初始化,优点时 可以写代码逻辑,缺点:所有对象的初始化效果是一样的.
注意:每new一个对象,代码块就会自动执行一次。
-
构造方法中初始化
构造方法初始化的优点:它是方法,可以带参数,所以可以定义多个,用参数区分;并且通过参数可以初始化不一样的效果。
-
如果以上三种都没有给成员变量进行初始化,就是使用默认初始化。
默认初始化值和数组中元素的默认初始化值一样。(0,0.0,false,null)
3.关于变量的作用域
变量分为局部变量和成员变量:
3.1 局部变量
- 声明在代码块或者方法中的变量称为局部变量
- 局部变量的作用域被局限在代码块或者方法的大括号中,就是局部范围。
- 同作用域或者重叠 的作用域中不能声明同名的局部变量。会报错:变量重复声明
3.2 成员变量
- 声明在类中的变量称为成员变量
- 成员变量的作用域被局限在类中.
- 同作用域中不能声明同名的成员变量。会报错:变量重复声明
但是重叠的作用域中可以声明同名的局部变量和成员变量。但是访问时,根据就近原则,局部变量较近,所以默认访问的就是局部变量。要访问到成员变量,必须加上this.也就是这种情况this.不能省略。
九、static
static用来修饰类中的成员。加了static修饰的成员属于类,叫做类成员或者静态成员。没有加static的成员属于对象,叫做实例成员或者对象成员。
类成员和实例成员的区别:
-
定义时:类成员需要加上static。实例成员不需要加。
-
访问时:类成员属于类,所以通过**类名.就可以访问。实例成员属于对象,所以必须通过对象.**才能访问。
-
具体成员的区别:
-
成员变量:实例成员变量属于对象,所以每个对象中都有一份。类成员变量属于类,因为类在全局只加载一次(一份),所以类成员变量在全局只有一份。
-
方法:实例方法属于对象,所以不同对象调用方法时效果可能不同。类方法属于类,所以通过类名调用效果永远是一样的。
成员变量和方法大部分是实例的。极少部分是静态的。
-
代码块:实例代码块在每个对象创建的时候都执行一次;类代码块只在类加载的时候执行唯一的一次。
一般只会用到静态代码块。
-
内部类:只区分访问方式即可。实例内部类通过对象.访问。静态内部类通过类名.就可以访问。
内部类一般也只会用到静态内部类。
-
public class StaticDemo{
//实例成员
int x;
int y;
public void showX(){
System.out.println("x=" + this.x);
}
public void showY(){
System.out.println("y=" + this.y);
}
{
System.out.println("初始化代码块(实例)");
}
class Inner{}
//类成员
static int m;
static int n;
public static void showM(){
System.out.println("m=" + StaticDemo.m);
}
public static void showN(){
System.out.println("n=" + StaticDemo.n);
}
static{
System.out.println("初始化代码块(静态/类)");
}
static class InnerStatic{}
}
public class App{
public static void main(String[] args){
//访问类成员
StaticDemo.m = 1;
StaticDemo.n = 2;
StaticDemo.showM();
StaticDemo.showN();
//类代码块不能访问:只在类加载的时候执行一次
StaticDemo.InnerStatic inner;
System.out.println("=====================");
//访问实例成员
StaticDemo demo = new StaticDemo();
demo.x = 10;
demo.y = 20;
demo.showX();
demo.showY();
//实例代码块不能访问:在每次实例化对象时候都会执行一次
demo.new Inner();
StaticDemo demo2 = new StaticDemo();
}
}
注意:
-
通过**对象.**也可以访问类成员。不要这么干。
-
关于在一个成员中去访问当前类或者对象另一个成员:
-
针对实例成员的话,用this. 而且this.可以省略
一个实例成员中可以访问其他的类成员。
-
针对类成员的话,还是用类名.,这是类名.可以省略
但是类成员中不能直接访问当前类中的实例成员。因为类成员中不存在this.
-
static类成员的使用场景:
-
如果某个数据或者对象在全局只存在一份,只是应该使用static静态成员变量去存储这个数据。
例如:System.out System.in
-
全局只有一个,或者说与对象无关的功能实现,应该使用static‘静态方法。
最常见的是一些工具方法,比如各种数学运算Math类中全是静态的方法。
-
全局要做的一些初始化操作,一般放在static静态初始化块中,就做一次。
比如:一些全局上下文数据的初始化。比如数据库驱动的加载,比如服务的预热等等。
十、package和import
1.package
1.1定义包
package包:package语句可以用来给类设置包名。
作用:把程序中的大量类分包去管理。
语法:
package 包名;
注意:
-
package语句写在源文件的第一行
-
package语句用来给当前这个源文件中的所有类设置包名
-
包名一般是点隔开的多级名称,每一级名称一般是标识符,而且小写
package com.edu.demo;
一般多级名称的起名原则是:公司域名反转.项目名.模块名
-
包名在编码上的体现就是package语句。
但是包在文件系统中的体现:需要把对应包的.java源文件(不必须)或者.class字节码文件(必须)放到每层包对应的文件夹中。Idea自动完成。
-
Idea中定义包有两种方式:
-
第一种:创建类的时候就指定包
new --> Java Class 输入:包名.类名
-
第二种:先创建包
new —> Package 输入:包名
-
1.2访问带包名的类
访问带包名的类:必须使用全类名:包名.类名 。 把类名叫做简单类名。
com.edu.demo.entity.Student stu = new com.edu.demo.entity.Student();
如果要简化类名的使用,就是使用简单类名:
- 第一种情况:如果当前类和要访问的类的包名相同,可以省略包名.
- 第二种情况:当前类和要访问的类不在一个包中,可以使用import导入
2.import
import语句用来导入一个类,当类导入以后,就可以直接使用简单类名。
import com.edu.demo.entity.Student;
import com.edu.demo.entity.*;
注意:
- import语句必须写类外边
- import导入时可以使用星号通配符
- Idea中使用类时,直接输入简答类名,会自动import导入
注意:java类库中有个特殊的包:java.lang 这个包中的所有类会自动导入,不需要import,就可以使用简单类名。比如:System , String等等
十一、访问控制
访问控制:就是控制类或者其成员在哪里可以访问,在哪里不可以访问。
有三个访问控制修饰词可以用来设置访问权限。
这三个访问控制修饰词把访问权限分为了四种:
- private:私有
- 缺省:一个词都不加
- protected:保护
- public:公有、公开
这四种权限的访问范围是:
类内部 | 同一个包中(类外部) | 在子类中(不在同一个包中) | 其他(不在同一个包中并且非子类) | |
---|---|---|---|---|
private | 可以访问 | 不可以 | 不可以 | 不可以 |
缺省 | 可以 | 可以 | 不可以 | 不可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
public | 可以 | 可以 | 可以 | 可以 |
访问控制修饰词可以修饰类以及类中的成员:
- 如果修饰类,只能使用public. 也就是类只有public的和缺省的。
- 如果修饰类中的成员,三个修饰词都可以,也就是有四种权限。
原则:在不影响功能实现的情况下,权限越小越安全。
十二、面向对象的三大特征
1.封装
封装:就是把不需要外界了解的实现细节隐藏起来,然后给外界提供必要的访问接口。
现阶段面向对象的封装原则:
- 把成员变量尽量私有化(private),然后如果那些成员变量需要外界访问,就提供必要的公开(public)的getter和setter方法,也就是如果要访问private私有的成员变量,直接调用对应的getter和setter方法即可。
- 方法一般都是给外界访问的,也就是public。但是有些方法只服务于类中的其他方法,那么就可以private私有。
package com.edu.entity;
public class Student {
//成员变量私有
private long stuNo;
private String stuName;
private String stuClass;
//提供public公开的getter和setter
//每个成员变量都可以有自己的getter和setter方法
//getter方法:获取成员变量的值
public long getStuNo(){
return this.stuNo;
}
//setter方法:给成员变量赋值
public void setStuNo(long stuNo){
this.stuNo = stuNo;
}
public String getStuName() {
return this.stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public String getStuClass() {
return this.stuClass;
}
public void setStuClass(String stuClass) {
this.stuClass = stuClass;
}
public Student() {
}
public Student(long stuNo, String stuName, String stuClass) {
this.stuNo = stuNo;
this.stuName = stuName;
this.stuClass = stuClass;
}
}
2.继承
2.1 继承
继承:如果一个类A包含了另一个类B中所有的成员,这时只需要让类A继承类B,就可以把类B中的成员都继承下来,也就是类A中就不需要再定义这些成员了。
所以:把被继承的类叫做父类/超类;把继承出来的类叫做子类/派生类。
继承的好处:
- 代码复用
- 有利于后期的扩展
扩展:继承其实准确来说,是在一个已有类的基础上扩展出来一个功能更强大的类。也就是继承以后子类中不仅包含了父类中所有的成员,还可以定义或者说扩展新的自己的成员。
一般父类比较抽象或者比较泛化;而子类比较具体。
比如:
- 人类是父类,学生类就是从人类扩展出来的子类。
- 动物类是父类,猫科动物是动物类的子类,狮子类是猫科动物类的子类
- 手机类是父类,智能手机就是手机类的子类。
public class Person{}
public class Student extends Person{}
2.2 关于子类对象的构造
创建子类对象时,先自动创建一个父类对象,然后这个父类对象包含在子类对象中。
2.2.1 子类中的this和super问题
-
子类中的this代表当前子类对象,所以this.既能访问子类扩展的成员,也能访问继承自父类的成员。this.可以省略。
this可以用来在一个构造中调用当前类的另一个构造。必须是第一条语句。
-
子类中的super代表当前子类对象中包含的那个父类对象。所以,super.只能访问继承自父类的成员。super.也可以省略。
注意:有一种情况下,访问成员时,如果把this.和super.省略了,会有问题。
当子类扩展的方法和父类中的方法相同(原型),这时this.访问的是子类中扩展的,super.访问的是继承自父类的。如果省略了,默认当作this.处理,也即是默认访问的是子类扩展的。如果非要访问继承自父类的,那么就不能省略super。
super可以用来在子类构造中调用父类构造。super也必须是第一条语句。
2.2.2 子类中的构造方法问题
因为子类对象中包含了一个父类对象,所以就要求在构造子类对象时,先构造一个父类对象,也就是要求在子类构造方法的第一条语句必须先调用下父类构造。
如果子类构造中第一条不去调用父类构造,系统也会自动默认调用一下父类的无参构造。
如果要自己在子类构造的第一条语句调用父类构造,使用super:
public Student(){
//第一条语句调用父类构造,如果没有显式调用父类构造,默认自动调用父类的无参构造
System.out.println("============Student()=============");
}
public Student(String name, int age, String gender, long stuNo, String stuClass){
//第一条语句调用父类构造,显式调用父类的构造
super(name, age, gender);
this.stuNo = stuNo;
this.stuClass = stuClass;
System.out.println("============Student(String,int,String,long,String)=============");
}
2.3 重写Override/Overwrite
重载:在一个类中,如果两个方法的方法名相同,形参列表不同,构成重载关系。
重写:在子类中,把继承自父类的某个方法重写重新实现一下。
重写的作用:因为有时父类中的方法实现不能满足子类的要求,这是子类就需要对这个方法进行重新实现。
所以:重新写的是方法的实现,方法的原型不变。
重写的要求:方法原型不变
-
方法名不变
-
方法形参列表不变
注意:参数类型能自动类型转换也可以
-
方法返回类型不变
注意:返回类型能自动类型转换也可以
-
方法的权限修饰词不变
注意:权限可以更大
-
方法抛出的异常类型不变
注意:异常可以抛出更少,只能是子集
public void fun(String x) throws 多个异常类型{
}
练习:
- 父类:手机类
- 成员变量:品牌、型号
- 方法:构造方法、打电话、发短信、show方法
- 子类:智能手机类
- 扩展的成员变量:内存大小、屏幕尺寸、颜色
- 方法:扩展上网的方法,重写打电话、发短信和show方法
- 测试:实例化子类对象,调用方法
2.4 上转型
java中的继承是一颗倒挂的树,父类在上,子类在下。
上转型:就是java可以自动把下边的子类对象转换为上边的父类类型。因为子类拥有父类的所有成员,所以把子类对象上转型为父类类型去使用,完全没有问题。
经常说:子类就是一种特殊的父类。 把学生Student当做Person人没问题,把智能手机当做手机使用没问题。
上转型的好处:
- 上转型为父类类型,类型更通用。父类类型可以引用各种子类对象。
- 上转型是多台的基础条件
package com.edu.entity;
public class UpConverterTest {
public static void main(String[] args) {
//把Student子类对象上转型为父类Person类型
Person p = new Student("李四", 24, "男", 1002, "软件2班");
//上转型以后调用方式时:调用的是子类中重写的那个
p.show();
}
}
编译时和运行时:
-
编译时
编译时检查类型,某个方法到底是否能调用,是在编译时决定的,所以能否调用看类型,不看对象。
-
运行时
调用方法的时候,具体选择调用哪个方法实在运行时决定的,而对象就是在运行时创建,也就是具体调用哪个方法,看的是对象,而不是类型。
上转型的缺点:
- 因为编译时类型检查,通过类型确定一个成员是否能访问。就会造成上转型以后,子类扩展的功能(成员)都无法访问
下转型:
上转型以后,如果有需要转回到原来的子类类型,就叫做下转型。下转型只能强制类型转换。
package com.edu.entity;
public class UpConverterTest {
public static void main(String[] args) {
//把Student子类对象上转型为父类Person类型
Person p = new Student("李四", 24, "男", 1002, "软件2班");
//上转型以后调用方式时:调用的是子类中重写的那个
p.show();
//p.learn("");//编译时检查类型中有没有要调用的方法
//p.stuNo = 1010;//编译时检查类型中有没有要访问的成员
//解决上转型的坏处
//下转型:先做判断
if(p instanceof Student){
Student s = (Student) p;
s.learn("java高级程序设计");
}else if(p instanceof Teacher){
Teacher t = (Teacher) p;
t.teach();
}
}
}
注意:为了不跑出类型转换一场,在下转型之前,先判断是否是目标类型的对象。
-
instanceof运算符
对象 instanceof 类型
结果是boolean类型,如果对象就是指定的类型,结果是true;否则结果是false
3.多态
多态:多态是一种现象,当父类类型的变量引用了不同子类类型的对象,在调用各种功能方法时,就会表现出不同子类重写的功能方法状态。引用的是哪个子类对象,表现得就是这种子类对象重写的状态。有多少种子类重写,就可以表现出多少种状态。
多态的基本条件:
-
继承
package com.edu.entity; /** * 人类:父类 */ public class Person { String name; int age; String gender; public Person(){ } public Person(String name, int age, String gender){ this.name = name; this.age = age; this.gender = gender; } public void show(){ System.out.println("********信息*********"); System.out.println("姓名:" + this.name); System.out.println("年龄:" + this.age); System.out.println("性别:" + this.gender); } }
package com.edu.entity; /** * 学生类:继承Person类 */ public class Student extends Person { long stuNo;//学号 String stuClass;//班级 public Student(){ } public Student(String name, int age, String gender, long stuNo, String stuClass){ super(name, age, gender); this.stuNo = stuNo; this.stuClass = stuClass; } }
-
重写
public void show(){ super.show();//在重写方法中可以使用super.调用父类中的对应方法 System.out.println("学号:" + this.stuNo); System.out.println("班级:" + this.stuClass); }
-
上转型
package com.edu.entity;
public class Application {
public static void main(String[] args) {
//Person p = new Student("张三",20,"男",1001,"软件1班");
Person p = new Teacher("赵老师",40,"女","T001",10);
p.show();
p.eat("烤肉");
p.sleep(6);
}
}
十三、abstract和final
1.abstract
抽象,可以用来修饰类和方法,定义抽象类和抽象方法。
1.1 抽象方法
抽象方法:定义方法时,加上abstract关键修饰,就是抽象方法。
抽象方法不能有实现。
public abstract void fun();
意义:
父类一般比较抽象,子类比较具体。父类种有些方法是无法实现或者没必要实现的,这时方法就必须声明为抽象方法,就可以不实现。
也就是父类中不实现的方法,其实是把实现留给子类种重写时实现。
1.2 抽象类
抽象方法必须在抽象类中。
抽象方法:定义类时,加上abstract关键修饰,就是抽象类。
抽象类不能实例化对象。是用来扩展子类,然后实例化子类对象。
public abstract class Pet{
public abstract void fun();
}
注意:子类种一般都会重写(也就是实现)父类中定义的抽象方法。如果不实现父类中的抽象方法,那么子类也必须声明为抽象类。
2.final
final可以修饰变量(局部变量和成员变量),方法,以及类。
-
修饰变量,叫做常量。
也就是初始化以后,不能再修改值。
修饰成员变量,成员变量就必须在以下一个地方进行初始化,而且只能选一个地方:
- 声明的同时初始化(static也一样)
- 初始化代码块(static也一样)
- 构造方法(只针对实例变量)
-
修饰方法
方法不能被子类重写
-
修饰类
类不能被继承(扩展)
final和abstract冲突,不能同时修饰类或者方法。
package com.edu;
public final class Test {
public final int X /*= 1*/;
public static final int Y /*= 2*/;
{
//this.X = 10;
}
static{
Y = 20;
}
public Test(){
this.X = 100;
}
public final void fun(){}
}
十四、接口
面向接口开发和设计程序,有利于实现组件化,低耦合,可插拔。
1.接口
接口:跟类相似,也是 一种类型。接口中所有成员变量默认都是公开静态常量(public static final),所有的方法模式都是公开的抽象方法(public abstract)。
接口的定义和使用都跟类几乎一样。并且一般接口对应的是继承关系中的父类。重点是接口对应的是多态中上转型的父类。
接口的作用:接口中包含的都是抽象方法(没有实现),只有方法的原型,也就是说接口其实定义的是一种行为(方法)标准。然后在实现类中按照标准去实现这些行为方法。
2.接口的定义
接口 的定义和类相似,类 用的是class关键字。接口使用interface关键字。
public interface 接口名{
//成员变量
int PI = 3.14;//默认就是public static final
//方法
void fun();//默认就是public abstract
}
package com.edu.demo;
/**
* 连接和执行sql语句的标准
*/
public interface ConnectionAndExecuteDatabase {
/**
* 连接数据库
* @param url
* @param username
* @param password
*/
void connect(String url, String username, String password);
/**
* 执行sql语句
* @param sql
* @return
*/
String execute(String sql);
/**
* 关闭连接
*/
void close();
}
3.接口的使用
接口的使用主要:
- 接口是一种类型
- 接口要给类去实现,就是实现接口中包含的抽象方法。
- 所以:接口会大量使用在多态的情形中,代替父类。
接口和类的区别:
-
定义时的关键字不一样:class interface
-
接口中只能包含静态常量和抽象方法
-
类之间继承以后,叫做父子关系。
接口和类之间叫做实现关系:接口对应父类,实现类对应子类。
-
类之间的继承只能单继承
class Child extends 父类{}
extends后边之后指定一个父类。
-
接口和类之间的实现关系允许 多实现:一个类可以实现多个接口。
class XxxImpl implements 接口1,接口2,...{}
implements后边可以指定多个接口
-
接口和接口允许多继承
子接口可以继承下来父接口中的所有成员。
interface 子接口 extends 父接口1,父接口2,...{}
-
抽象类的限制,接口都有。不能实例化对象。接口比抽象类更抽象。
类实现接口:
public class 实现类的类名 implements 接口名{
}
package com.edu.demo;
/**
* 针对于MySQL数据的接口实现
*/
public class MySQLImpl implements ConnectionAndExecuteDatabase {
@Override
public void connect(String url, String username, String password) {
System.out.println("连接MYSQL数据库,地址是" + url + ",用户名是:" + username + ",密码是:" + password);
}
@Override
public String execute(String sql) {
System.out.println("在MySQL数据上执行:" + sql);
return "MYSQL执行结果";
}
@Override
public void close() {
System.out.println("关闭和MySQL数据库的连接");
}
}
package com.edu.demo;
public class OracleImpl implements ConnectionAndExecuteDatabase{
@Override
public void connect(String url, String username, String password) {
System.out.println("连接Oracle数据库,地址:" + url + ",用户名:" + username + ",密码:" + password);
}
@Override
public String execute(String sql) {
System.out.println("在Oracle数据上执行:" + sql);
return "ORACLE执行结果";
}
@Override
public void close() {
System.out.println("关闭和Oracle数据库的连接");
}
}
package com.edu.demo;
public class App {
public static void main(String[] args) {
//请连接数据库,并执行select * from student语句
ConnectionAndExecuteDatabase ce = new OracleImpl();//可插拔
ce.connect("jdbc:mysql://127.0.0.1:3306/db1", "root", "123456");
ce.execute("select * from student");
ce.close();
}
}
接口是一种标准,先定义好标砖,再去按照标准进行不同的实现:
-
低耦合
与接口相关,与实现无关。降低了 和实现的耦合度。
-
可插拔
随意切换实现类 对象,都不影响代码的编写和运行。
十五、Java中的异常处理机制
异常:就是运行过程中发生的一种异于正常情况的状态。比如:下标越界异常、空指针异常、SQL异常、类型转换异常、解析异常、输入不匹配异常、无效参数异常、网络异常、找不到文件异常、读写异常等等。以及自定义的业务异常。
异常处理:如果异常发生以后,不进行捕捉处理,java程序就只在异常发生的地方直接终止运行,并打印出异常信息。为了保证程序不会异常终止,应该在异常可能发生的地方捕捉并处理异常。
1 捕捉处理异常
1.1 基本的try和catch
try{
//有可能发生异常的代码块
}catch(异常类型 变量名){
//捕捉到了异常,异常处理代码块
}
package com.edu.demo;
import java.util.Scanner;
public class ExceptionTest {
public static void main(String[] args) {
int[] arr = {10,100,40,58,90};
Scanner input = new Scanner(System.in);
System.out.print("请输入一个下标");
int index = input.nextInt();
try{
int num = arr[index];
System.out.println(index + "下标位上的数是:" + num);
}catch(IndexOutOfBoundsException e){
System.out.println("抓到异常了:" + e.getMessage());
}
}
}
1.2 一个try可以多个catch
一段代码块可能抛出多种类型的异常,这是就需要 多个catch去捕捉多种类型的异常。
package com.edu.demo;
import java.util.InputMismatchException;
import java.util.Scanner;
public class ExceptionTest {
public static void main(String[] args) {
int[] arr = {10,100,40,58,90};
Scanner input = new Scanner(System.in);
try{
System.out.print("请输入一个下标:");
int index = input.nextInt();
int num = arr[index];
System.out.println(index + "下标位上的数是:" + num);
}catch(IndexOutOfBoundsException e){
System.out.println("抓到下标越界异常了:" + e.getMessage());
}catch(InputMismatchException e){
System.out.println("抓到输入不匹配异常了:" + e.getMessage());
}
}
}
1.3 try最后还可以有finally代码块
finally代码块不管有没有抛出异常都会执行。
package com.edu.demo;
import java.util.InputMismatchException;
import java.util.Scanner;
public class ExceptionTest {
public static void main(String[] args) {
int[] arr = {10,100,40,58,90};
Scanner input = null;
try{
input = new Scanner(System.in);//这行会打开输入流资源
System.out.print("请输入一个下标:");
int index = input.nextInt();
int num = arr[index];
System.out.println(index + "下标位上的数是:" + num);
}catch(IndexOutOfBoundsException e){
System.out.println("抓到下标越界异常了:" + e.getMessage());
}catch(InputMismatchException e){
System.out.println("抓到输入不匹配异常了:" + e.getMessage());
}finally {
System.out.println("finally代码块一定执行!!!");
//一般做:在try中打开的资源,不管有没有抛出异常,在trycatch快结束的时候都应该关闭或者回收资源
if(input != null){
input.close();
}
}
}
}
1.4 抛出异常以后的执行流程问题
当一行代码抛出异常以后,先看当期位置能不能捕捉处理,如果能直接catch、finally继续往下;如果不能捕捉处理,执行下当前位置的finally,当前方法直接return返回到调用的地方,然后看调用的地方能不能捕捉处理,按照这个过程一直根据方法的调用往上找,直到找到能捕捉处理异常的地方。如果找到main中还不能补助处理,那就程序终止。
2 异常类型
异常本质就是对象,所以肯定类型。
所有的异常都必须继承Throwable类。
Throwable
-
Exception
Exception下边的子类一般是可处理的异常。
Exception又分为两种:
-
RuntimeException
运行时异常,他自己或者她的子类,这些异常可以捕捉处理,但是不建议捕捉处理,应该在异常抛出的地方通过条件判断去避免这种异常方法。
比如:下标越界、空指针等等
-
其他:也叫作受检异常
比如 SQL异常等等
-
-
Error
Error下边的子类一般是无法处理的异常,也叫做眼中错误异常。比如虚拟机异常。这种异常不要捕捉,不要处理,就让程序直接终止。
3 自定义异常
系统定义好的异常类型,叫做系统异常。比如下标越界、空指针等等。系统异常会在发生的时候自动抛出来。
自定义异常是与开发的项目业务相关的异常,需要自己定义。自定义异常不仅需要自己定义,而且在发生的时候,需要自己抛出。
自定义异常一般不建议继承Throwable,而选择继承它的子类Exception。
public ArrayOverflowException extends Exception{
//定义下带参构造,初始化继承自父类的成员message
public ArrayOverflowException(){}
public ArrayOverflowException(String message){
super(messsage);
}
}
4.异常的抛出和声明
4.1 异常的抛出throw
throw 异常对象;
throw new Exception();
4.2 异常的声明throws
public void fun() throws 异常类型1,异常类型2 {}
十六、Java系统类库
1.按照包分类:
-
java.lang
这个包中是最基础的类,System、String (StringBuilder\StringBuffer)、8个基本数据类型的包装类、Math、Thread多线程等等
-
java.util
这个包中是一些工具类,Scanner 、 Random、Date/Calendar 、Arrays、集合框架相关的借口和类、Timer定时器等等
-
java.awt和javax.swing
这两个包中都是GUI图形化界面变成相关的接口和类
-
java.sql
数据库变成相关的接口和类
-
java.io和java.nio
这两个包中都是输入输出相关的接口和类
-
java.net
网络编程相关的接口和类
-
org.w3c或者org.xml
这是w3c相关的接口和类。解析生成xml
2.八个基本数据类型的包装类
int short long byte double float char boolean
Integer Short Long Byte Double Float Character Boolean
Number类是前六个的父类。
一般基本数据类型和引用数据类型可以自动转换,java中把这种自动转换称为自动装箱和自动拆箱。
package com.edu;
public class IntegerDemo {
public static void main(String[] args) {
//Integer
int a = 1;
Integer ia = new Integer(1);
//自动装箱 int ---> Integer
Integer ib = 2;
//自动拆箱 Integer ---> int
int b = new Integer(2);
//字符串转换为对应的基本数据类型
// "12" ---> 12
int num = Integer.parseInt("12");
//"3.14" 3.14
double dnum = Double.parseDouble("3.14");
// 12 ---> "12"
String str = Integer.toString(12);
String str2 = 12 + "";
//上转型
Number n = new Integer();
}
}
3.String字符串
String是java中基本字符串类型,java还有两种字符串类,StringBuilder,StringBuffer
这三个都实现了同一个接口,字符序列接口 CharSequence。
package com.edu;
public class StringDemo {
public static void main(String[] args) {
//String :所有new出来的对象都在堆区
String str = new String("Hello");
String str2 = new String("Hello");
//引用数据类型判断相等,可以使用==,也可以调用对象中的equals方法
//如果是==。判断的是 是否是同一个对象
System.out.println(str == str2);//false
//equals是一个方法,它到底怎么判断相等,要看方法的代码怎么实现的
//String类中的equals方法的实现是:判断两个字符串对象中的字符序列是否一样
System.out.println(str.equals(str2));//true
//String比较特殊,String可以引用常量池中的字符串常量, 同一个常量对象,在常量池中只存在一份
String str3 = "Hello";
String str4 = "Hello";
System.out.println(str == str3);//false
System.out.println(str.equals(str3));//true
System.out.println(str3 == str4);//true
System.out.println(str3.equals(str4));//true
//String中的方法:字符串处理方法,比如字符串长度,字符串大写转换,字符串拆分,字符串替换,字符串截取,字符串中某个字符的获取等等
String filename = "HelloWorld.java";
System.out.println("charAt(5) : " + filename.charAt(5) );
System.out.println("长度:" + filename.length());
System.out.println("是否是java源文件:" + filename.endsWith(".java"));
System.out.println("截取后缀:" + filename.substring(filename.lastIndexOf(".")));
//String字符串不可变,String中对字符串进行变化操作的方法。都是生成一个新的字符串返回,源字符串没变
String newFIlename = filename.toLowerCase();//转全小写
System.out.println(newFIlename);
}
}
4.Date日期
java.util.Date 和 java.util.Calendar 和 DateFormat
package com.edu;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;
public class DateDemo {
public static void main(String[] args) {
Date currentDate = new Date();//不带参的构造,初始化的是当前系统时间
Date birthday = new Date(90,0,15);//参数中年从1900开始算,月从0开始算
Date orderTime = new Date(123,0,15,8,10,10);
System.out.println(currentDate);
System.out.println(birthday);
System.out.println(orderTime);
long currentTime = System.currentTimeMillis();//时间戳(毫秒数)
long orderTime2 = orderTime.getTime(); // Date ---> long
Date orderTime3 = new Date(orderTime2);// long ---> Date
Calendar calendar = Calendar.getInstance();//getInstance方法会实例化一个Calendar对象,并返回。初始化的也是当前系统时间
System.out.println(calendar);
//Calendar中的方法
Date cd = calendar.getTime();// Calendar ---> Date
long ct = calendar.getTimeInMillis();// Calendar ---> long
calendar.setTime(new Date());// Date ---> Calendar
calendar.setTimeInMillis(orderTime2);// long ---> Calendar
//Calendar中有大量的get和set方法
calendar.set(Calendar.YEAR, 2022 );
calendar.set(Calendar.MONTH, 3 );
calendar.set(Calendar.DATE, 3 );
calendar.set(2020,0,1);
calendar.set(2020,0,1,8,10,0);
System.out.println(calendar.get(Calendar.YEAR));
System.out.println(calendar.get(Calendar.MONTH)+1);
System.out.println(calendar.get(Calendar.DATE));
//日期格式
//如果日期按照某个格式组织为一个字符串,这是需要转换为Date类型
Scanner input = new Scanner(System.in);
System.out.print("请输入日期时间(yyyy-MM-dd HH:mm:ss):");
String dateStr = input.nextLine();
//按照格式解析日期时间字符串
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date date = format.parse(dateStr);//String ---> Date
String str = format.format(date);//Date ---> String
System.out.println(str);
} catch (ParseException e) {
e.printStackTrace();
System.out.println("格式不正确");
}
}
}
5.Object
Object也是类库中定义的类,这个类是所有类的父类。如果定义类时,没有指定父类,默认以Object作为父类。
根据上转型的概念,Object类型的变量可以引用任何对象。Object可以实现超级类型通用。
根据继承的概念,Object类中的成员会被所有类继承下去。
Object中的成员:
-
toString: toString方法是把对象相关信息拼接为一个字符串返回。
Object类中已经实现了toString,拼接出来的字符串是: 对象全类名@对象地址。
一般都会重写toString方法,以方便输出对象信息。
-
equals: equals是一个用来判断两个对象是否相等的方法
== 和 equals的区别:
当使用==判断两个是否相等,其实判断的是是否是同一个对象。
Object已经实现了equals方法,但是他的实现也是判断是否是同一个对象。一般都会重写equals方法。
package com.edu;
public class Student {
String stuno;
String stuname;
public Student(){}
public Student(String stuno, String stuname) {
this.stuno = stuno;
this.stuname = stuname;
}
//重写父类Object的toString
@Override
public String toString() {
return "学号:" + this.stuno + ",姓名:" + this.stuname;
}
public boolean equals(Object other){
if(this == other){
return true;
}
if(other == null){
return false;
}
//学号和姓名相同就认为两个对象相等
if(other instanceof Student){
Student that = (Student) other;
if(this.stuno.equals(that.stuno) && this.stuname.equals(that.stuname)){
return true;
}
}
return false;
}
}