Hello
- 新建java文件,后缀名为.java,文件名要和类名保持一致
public class Hello{ //Hello是类
public static void main(String[] args){ //main是方法,方法的括号里存放参数
Syetem.out.print("Hello,py!")
}
}
- 编译
在java文件目录下,使用 javac Hello.java 进行编译,生成class文件 - 运行
运行java class文件,使用java Hello 运行
PS:
- java对大小写是敏感的;
- 文件名和类名要保持一致;
- 不能使用中文符号。
源代码经过编译,生成字节码,然后经解释器一句一句运行出来。
一、IEDA开发
快捷操作
psvm,生成一个main方法
sout,生成输出语句
Ctrl + Shift + Enter 快速补全分号
Ctrl+]或者Ctrl+[ 定位匹配大括号
Alt+Enter,new一个类后自动返回类对象定义
注释
单行注释://
多行注释:/* */
文档注释:/** */ 操作方法:/**回车
二、基础语法
2.1 标识符和关键字
2.1.1 关键字
java所有的组成部分都需要名字。类名、变量名、方法名都被称为标识符。
2.1.2 static关键字
public class Worker {
private static int age;
private double score;
public void run(){
go(); //非静态方法可以调用静态方法
}
public static void go(){
}
public static void main(String[] args) {
Worker a = new Worker();
a.score = 100; //非静态变量需要使用“对象名.变量名”
Worker.age = 18; //类中的静态变量推荐使用“类.变量名”
a.run();
go();
}
}
- 静态代码块
public class Boss {
//2:可以用来赋初值
{
System.out.println("匿名代码块");//匿名代码块
}
//1:只执行一次
static {
System.out.println("静态代码块");
}
//3
public Boss() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Boss boss = new Boss();
}
}
运行结果:
静态代码块
匿名代码块
构造方法
- 静态导入包
//静态导入包,
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(random());
System.out.println(PI);
System.out.println(Math.max(1,2)); //没有静态导入max这个方法,就要以“类名.方法名”的方式使用
}
}
2.1.3 标识符注意点
- 标识符只能以字母、$、_ 三种符号为开头
- 不能使用关键字作为变量名或方法名
2.2 数据类型
java是强类型语言,所有变量都必须下定义才能使用
2.2.1 数据类型的分类
基本数据类型
- 数值类型:
整数:byte(1字节)、short(2字节)、int(4字节、默认的)、long(8字节)
浮点数:float(4字节)、double(8字节、默认的)
- 字符类型:char(2字节)
- boolean类型:值只有true和false,占1位
int最常用,long类型要在数字后面加一个L,float要在数字后面加一个F
引用数据类型
类、接口、数组
2.2.2 扩展
- 进制:二进制0b、八进制0、十六进制0x
- 最好完全避免用float进行比较,因为float存在舍入误差,如果需要进行计算,并且不能有误差,可以使用java的BigDecimal类
- 转义字符:\t 制表符 \n 换行 \r回车
2.2.3 类型转换
低 ---------------------------------------------------------------高
byte,short,char→ int → long → float → double
强制转换: (类型)变量名 高→低
自动转换: 低→高
注意点:
- 运算中,不同类型的数据要先转换,再运算;
- 转换时可能存在内存溢出、精度变化的问题;
- 不能对布尔值进行转换;
- 高容量转换到低容量的时候,需要使用强制转换。
2.3 变量
代表一块空间,位置是确定的,存什么东西是可以变化的
- java变量是程序中最基本的存储单元,包括:变量名、变量类型和作用域;
- java是一种强类型语言,每个变量必须声明其类型。
type varName [=value];
//数据类型 变量名 = 值;
//可以使用逗号隔开来生命多个同类型变量。
2.3.1 变量作用域
- 类变量
public class Demo01 {
static String str; //str为类变量,在这个类中都可以调用
public static void main(String[] args) {
System.out.println(str);
}
}
作用域是类,可以直接调用
- 实例变量
位于方法外边,类里边,从属于对象,如果不进行初始化,默认为该数据类型的默认值(所有的数字类型为0 ,布尔值为false,基本类型外的其他类型为null)
public class Demo1 {
int age; //age属于实例变量,从属于Demo1这个类,作用域要大于局部变量
String name;
public static void main(String[] args) {
Demo01 demo1 = new Demo1(); //变量类型 变量名 = new Demo1()
System.out.println(demo1.age); //通过demo1来使用age这个实例变量
System.out.println(demo01.name);
}
}
运行结果:0
null
作用域为方法外,类里边,想要在方法中调用,需要new
- 局部变量
public class Variable{
static int a = 0; //类变量,有个static
String str = "hello"; //实例变量,位于方法外边,类的里边
public void method(){
int i = 0; //局部变量,位于方法里边,必须生命和初始化值
}
}
作用域为方法内部,必须声明和初始化值
2.3.2 常量
或叫做:静态变量,可以看做是一种特殊的变量,初始化后它的值不会发生变化
final 常量名 = 值;
2.3.3 变量的命名规范
- 类成员变量:首字母小写和驼峰原则,helloWorld,lastName
- 局部变量:首字母小写和驼峰原则
- 常量:大写字符和下划线,MAX,MAX_VALUE
- 类名:首字母大写和驼峰原则,Hello,HelloWorld
- 方法名:首字母小写和驼峰原则,main,mainTest
2.4 运算符
2.4.1 基础运算符
算数运算符:+, -, *, /, ,++, --,%
赋值运算符:=
关系运算符:>, <, >-, <=, ==, !=, instanceof
逻辑运算符:&&, ||, ! //与、或、非
位运算符:&, |, ^, ~, >>, <<, >>>
条件运算符:?, :
扩展赋值运算符:+=, -+, *=, /=
- 运算时,若存在long类型的变量,结果也是long类型的;若存在double类型的,结构也是double类型的;否则为int类型。
- 关系运算符返回的结果:true、false
2.4.2 自增、自减运算符
public class Demo {
public static void main(String[] args) {
int a = 3;
int b = a++; //先给b赋值,再自增
int c = ++a; //先自增,再给c赋值
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
运行结果:5
3
5
幂运算 2^3,需要借助工具类进行操作
public class Demo {
public static void main(String[] args) {
double pow = Math.pow(2, 3); //借助Math类中的pow方法
System.out.println(pow);
}
}
运行结果:8.0
短路运算
public class Demo {
public static void main(String[] args) {
int a = 10;
boolean b = (a<6)&&(a++<13);
//与运算,第一个已经不满足,就不进行后面的运算,所以a没有自加
System.out.println(a);
System.out.println(b);
}
}
运行结果:10
false
2.4.3 位运算符
A = 0011 1100
B = 0000 1101
A&B = 0000 1100 //相同位置都为1,才是1
A|B = 0011 1101 //有1则为1
A^B = 0011 0001 //相同为0,否则为1
~B = 1111 0010 //取反
<<左移 相当于*2
>>右移 相当于/2
public class Demo05 {
public static void main(String[] args) {
System.out.println(2<<3);//二进制中,左移三位,0000 0010→0001 0000
}
}
运行结果:16
2.4.4 条件运算符和扩展赋值运算符
a+=b; //a=a+b
a-=b; //a=a-b
字符串连接符
public class Demo {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println(""+a+b);
//在+左侧出现了String类型,就会把其他操作数都转换成String并连接
System.out.println(a+b+"");
//在+右侧出现了String类型,左侧依然进行运算
}
}
运行结果:1020
三元运算符
x ? y : z;
//如果x==true,则结果为y,否则为z
2.5 包机制
- 用于区别类名的命名空间,包的本质等同于文件夹。
- 一般利用公司域名倒置作为包名,com.baidu.www
- import 导入包
2.6 JavaDoc
生成.class文件后,使用javadoc命令生成自己的API文档
javadoc -encoding UTF-8 -charset UTF-8 HelloWorld.class
-encoding 编码方式
-charset 字符集
参数信息
@author 作者名
@version 版本号
@since 指明需要最早只用的jdk版本
@param 参数名
@return 返回值情况
@throws 异常抛出情况
三、Java流程控制
3.1 用户交互scanner
用于获取用户输入的工具类,java.util.Scanner
- 基本语法
Scanner s = new Scanner(System.in);
通过Scanner类的next()与nextLine()方法获取输入的字符串,在读取前一般需要使用hasNext()与hasNextLine()判断是否还有输入的数据。
3.1.1 基本方法的使用
- next( )方法使用:
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
/*创建一个扫描器对象,用于接收键盘数据*/
Scanner scanner = new Scanner(System.in); //new一个Scanner对象,并将其封装成scanner对象
System.out.println("使用next方式接收:");
/*判断是否有用户输入的字符串*/
if (scanner.hasNext()){
String str = scanner.next(); //使用next方法接收用户输入
System.out.println("输入端内容为:" + str);
}
scanner.close(); //凡是属于IO流的类,使用结束后都要关闭
}
}
运行结果:
使用next方式接收:
Hello py!
输入端内容为:Hello
- nextLine( )方法使用:
import java.util.Scanner;
public class Demo02 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("使用nextline方式接收:");
if (scanner.hasNextLine()){
String str = scanner.nextLine();
System.out.println("输出的内容为:" + str);
}
scanner.close();
}
}
运行结果:
使用next方式接收:
Hello py!
输入端内容为:Hello py!
next()不能得到带有空格的字符串;nextLine()是以Enter为结束符,可以获得空白
3.1.2 scanner进阶用法
- 判断输入数字类型:
import java.util.Scanner;
public class Demo04 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int i = 0;
float f = 0.0f;
System.out.println("请输入整数:");
/*使用hasNextInt判断输入是否为整数*/
if (scanner.hasNextInt()){
i = scanner.nextInt();
System.out.println("输入整数为:" + i);
}else {
System.out.println("输入不是整数!");
}
System.out.println("请输入小数:");
/*使用hasNextInt判断输入是否为整数*/
if (scanner.hasNextFloat()){
f = scanner.nextFloat();
System.out.println("输出小数为:" + f);
}else {
System.out.println("输入不是小数");
}
}
}
- 求多个输入数字的和,并求平均
import java.util.Scanner;
public class Demo05 {
public static void main(String[] args) {
//输入多个数字,求和、求平均,每输入一个数字用回车确认,输入非数字结束输入
Scanner scanner = new Scanner(System.in);
double sum = 0; //求和
int m = 0; //计算输入了多少个数字
/*通过循环判断是否还有输入,并进行求和、统计*/
while (scanner.hasNextDouble()){
double v = scanner.nextDouble();
m++;
sum = sum + v;
System.out.println("当前你输入的第"+m+"个数字为:"+v+"当前求和结果为:"+sum);
}
System.out.println("和为:" + sum);
System.out.println("平均数为:" + (sum/m));
scanner.close();
}
}
3.2 顺序结构
Java的基本结构就是顺序结构,除非特别指明,否则所有指令都是按从上到下的顺序执行。
3.3 选择结构
使用 if 和 switch 进行选择
3.3.1 if单选择结构
- 基本语法
if(布尔表达式){
//如果布尔表达式为true则执行括号内的语句,否则直接跳过if
}
- 单选择练习
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
if (s.equals("py")){
System.out.println("py");
}
System.out.println("End");
scanner.close();
}
}
3.3.2 if双选择结构
基本语法
if(布尔表达式){
//如果布尔表达式的值为true
}else{
//如果布尔表达式的值为false
}
- 双选择练习
import java.util.Scanner;
public class Domo02 {
public static void main(String[] args) {
/*考试分数大于等于60分及格,否则不及格*/
Scanner scanner = new Scanner(System.in);
int score = scanner.nextInt();
if (score>=60){
System.out.println("考试及格");
}else{
System.out.println("考试不及格");
}
scanner.close();
}
}
3.3.3 if多选择结构
- 基本语法:
if(布尔表达式1){
//如果布尔表达式1的值为true
}else if(布尔表达式2){
//如果布尔表达式2的值为true
}else if(布尔表达式3){
//如果布尔表达式3的值为true
}else{
//如果以上布尔表达式的值都不为true
}
- 多选择练习
import java.util.Scanner;
public class Demo03 {
public static void main(String[] args) {
/*每10分提供一个评级*/
Scanner scanner = new Scanner(System.in);
while(scanner.hasNextInt()){
int score = scanner.nextInt();
if (score==100){
System.out.println("评分等级:A+");
}else if (score>=90 && score<100){
System.out.println("评分等级:A");
}else if (score>=80 && score<90){
System.out.println("评分等级:B");
}else if (score>=70 && score<80){
System.out.println("评分等级:C");
}else if (score>=60 && score<70){
System.out.println("评分等级:D");
}else if (score>=0 && score<60){
System.out.println("评分等级:不及格");
}else {
System.out.println("输入不合法");
}
}
scanner.close();
}
}
3.3.4 嵌套的if结构
在一个if或者if else语句里是可以使用if或者if else语句的。
- 基本语法
if(布尔表达式1){
//如果布尔表达式1的值为true
if(布尔表达式1){
//如果布尔表达式2的值为true
}
}
- 嵌套选择练习
import java.util.Scanner;
public class Demo04 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int score = scanner.nextInt();
if (score==100){
System.out.println("满分");
}else {
if (score>60){
System.out.println("及格");
}else{
System.out.println("不及格");
}
}
scanner.close();
}
}
3.3.5 switch多选择结构
判断一个变量与一系列值中的某个值是否相等,每个值称为一个分支。
- switch语句中的变量类型可以是:
byte、short、int、char或者String
switch(表达式){
case value1:
//执行语句1
break; //可选
case value2:
//执行语句2
break; //可选
default: //可选
//执行语句3
}
- switch练习
public class SwitchDemo01 {
public static void main(String[] args) {
char grade = 'F';
switch (grade){
case 'A':
System.out.println("优秀");
break;
case 'B':
System.out.println("及格");
break;
case 'C':
System.out.println("不及格");
break;
default:
System.out.println("未知");
}
}
}
- 如果不加break,可能存在一个case穿透的问题,假设满足B,而B没有break,就会输出"及格"和"不及格",所以每写一个case,在后面都要加一个break。
3.4 循环结构
3.4.1 while循环
while(布尔表达式){
//循环内容
}
- while循环练习
public class WhileDemo01 {
public static void main(String[] args) {
/*计算1~100的和*/
int i = 0;
int sum = 0;
while(i<=100){
sum = sum + i;
i++;
}
System.out.println(sum);
}
}
运行结果:
5050
3.4.2 do…while循环
do…while语句至少执行一次;
while先判断后执行,do…while先执行后判断。
do{
//执行语句
}while(布尔表达式);
- do…while循环练习
public class DoWhileDemo01 {
public static void main(String[] args) {
int i = 0;
do {
i++;
System.out.println("第一次:"+i);
}while(i<0);
}
}
运行结果:
第一次:1
3.4.3 for循环
for循环执行的次数在执行前是确定的。
- for循环执行流程:
- 最先执行初始化步骤。可以声明一种类型,但可以初始化一个或多个循环控制变量,也可以是空语句;
- 检测布尔表达式的值,如果为true,则执行循环体,如果为false,循环终止,开始执行循环体后面的语句;
- 每执行一次循环,更新一次控制变量;
- 再次检测布尔表达式的值,循环执行上面的过程。
- 基本语法
for(初始化;布尔表达式;更新){
//代码语句
}
- for循环练习1
public class ForDemo02 {
public static void main(String[] args) {
/*计算0-100之间奇数偶数的和*/
int oddsum = 0;
int evensum = 0;
for (int i = 0; i <= 100; i++) {
if (i%2!=0){
oddsum+=i;
}else{
evensum+=i;
}
}
System.out.println("奇数和为:"+oddsum);
System.out.println("偶数和为:"+evensum);
}
}
运行结果:
奇数和为:2500
偶数和为:2550
- for循环练习2
public class ForDemo03 {
public static void main(String[] args) {
/*循环输出1-100之间能被5整除的数字,每显示3个数字*/
for (int i = 0; i <= 100; i++) {
if (i%5==0){ //选出能被5整除的数字
System.out.print(i+"\t");
}
if (i%(15)==0){ //第三个数字能被15整除
System.out.print("\n"); //换行
}
}
}
}
换行可以用两句话
System.out.print("\n");
System.out.println();
- for循环练习3
/**
* 1.使用for循环输出一个1-9;
* 2.输出两个9的循环相乘;
* 3.去掉重复的内容
* 4.调整输出格式
*/
public class ForDemo04 {
public static void main(String[] args) {
for (int j = 1; j <= 9; j++) {
for (int i = 1; i <= j; i++) {
System.out.print(i + "*" + j + "=" + i*j +"\t");
}
System.out.println();
}
}
}
运行结果:
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
- 增强for循环
主要用于数组或集合
基本语法
for(声明语句:表达式){
//代码句子
}
//声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配,作用域为for循环内,其值与此时数组元素的值相等。
//表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
增强for循环练习:
public class ForDemo05 {
public static void main(String[] args) {
int i[] = {10,20,30};
for (int x:i){ //把数组i全部赋给x,x的数据类型和i[]相同
System.out.println(x);
}
}
}
输出结果:
10
20
30
3.5 break&continue
break用于强制退出退出循环,不执行循环中剩余的语句;
continue用于终止某次循环过程(跳过某次循环后继续执行下一次循环)。
3.5.1 break练习
public class BreakDemo01 {
public static void main(String[] args) {
for (int i = 1; i <= 9; i++) {
if (i==7){
break; //i=7的时候退出循环,但该方法依然可以执行
}
System.out.print(i);
}
}
}
运行结果:
123456
3.5.2 continue练习
public class BreakDemo01 {
public static void main(String[] args) {
for (int i = 1; i <= 9; i++) {
if (i==7){
continue; //跳过i=7
}
System.out.print(i);
}
}
}
运行结果:
12
3.6 流程控制练习
练习1
/*输出1-20之间所有的质数*/
/*检验该数能否被自己和1之外的数整除即可,如果能整除并且不是自己,跳过该数字*/
public class LabelDemo01 {
public static void main(String[] args) {
for (int i = 2;i<=20;i++){
for (int j = 2;j<=i;j++){
if (i%j==0 && i!=j){ //能整除其他数,并且不是自己
break; //如果能整除,跳出循环,执行i++
} //如果不满足该if,执行j++
else if (i==j){ //直到满足i==j,
System.out.print(i + "\t");
}
}
}
}
}
练习2
public class TestDemo01 {
public static void main(String[] args) {
//打印三角形
for (int i = 1; i <= 5; i++) { //5行
for (int j = 5; j >= i; j--){ //先打印每行前面的空白
System.out.print(" ");
}
for (int j = 1; j <= i ; j++) { //打印三角形的左半边
System.out.print("*");
}
for (int j = 1; j <i ; j++) { //打印三角形的右半边
System.out.print("*");
}
System.out.println();
}
}
}
运行结果:
*
***
*****
*******
*********
四、Java方法
4.1 方法的概念
System.out.println()
//System是一个类,out是一个对象,println()是一个方法
- Java方法是语句的集合
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
一个方法只完成一个功能
4.2 方法的定义和调用
方法类似于其他语言的函数
4.2.1 方法的组成部分
方法定义时,参数为形参,要在调用方法的时候将实参赋值给形参
实参和形参的数据类型要一一对应
修饰符 返回值类型 方法名(参数类型 参数名){
…
方法体
…
return 返回值; //如果方法存在返回值,必须return返回,如果是void就不需要了
}
/**
* 修饰符:public、static等
* 返回值类型:有些方法没有返回值,这种情况下的类型为void
* 参数类型:参数像是一个占位符,方法被调用时,传递值给参数,这个值为实参或变量
* 方法体:具体的语句,定义该方法的功能
*/
public class Demo01 {
public static void main(String[] args) {
int sum = add(1, 2); //调用时,传值给形参,这里的1和2为实参
System.out.println(sum);
}
public static int add(int a, int b){ //add方法的两个参数a和b均为形参
return a+b;
}
}
- 方法练习
public class Demo02 {
public static void main(String[] args) {
int compare = compare(10, 20); //1.传参 3.将返回值赋值给compare
System.out.println(compare);
}
public static int compare(int a, int b){
int maxnum = 0;
if (a==b){
System.out.println("a=b");
return 0; //return可以终止方法
}
if (a>b){
maxnum = a;
}else {
maxnum=b;
}
return maxnum; //2.传参后得到返回值maxnum
}
}
4.2.2 方法调用
- 调用方法:对象名.方法名(实参列表)
- Java有两种调用方法的方式,根据方法是否有返回值进行选择。
- 方法有返回值的时候,方法调用通常会被当做一个值。例如:
int larger = max(30, 40);
- 如果方法返回值为void,方法调用一定是条语句。例如:
System.out.println("Hello!");
方法调用的两种方式
- 静态方法 static 关键字,使用 对象名.方法名() 的方式调用
//在Student类中定义了一个say静态方法
public class Student {
public static void speak(){
System.out.println("说话");
}
}
//在Demo02类中调用上面定义的say方法
public class Demo02 {
public static void main(String[] args) {
Student.speak(); //类名.方法名()
}
}
- 非静态方法的调用,要使用 new 关键字将类实例化
//在Student类中定义一个非静态的方法
public class Student {
public void speak(){
System.out.println("说话");
}
}
//在Demo02类中调用上面定义的Student方法
public class Demo02 {
public static void main(String[] args) {
//先使用new将方法所在的类进行实例化
//对象类型 对象名 = 对象值;
Student student = new Student();
student.speak();
}
}
- 在同一个类中,一个静态方法调用非静态方法会报错
//一个已经存在的方法肯定是不能调用一个还没有存在的方法的,所以直接调用b方法的时候会报错
public class Demo02 {
public static void main(String[] args) {
Student student = new Student();
student.speak();
}
//静态方法,和类一起加载
public static void a(){
b();
}
//非静态方法,实例化之后才存在
public void b(){
}
}
4.2.3 值传递和引用传递
Java的本质是值传递
引用传递:传递对象,其本质还是值传递
public class Demo05 {
public static void main(String[] args) {
People people = new People(); //1.实例化People,定义为people
System.out.println(people.name); //返回null
Demo05.change(people);
System.out.println(people.name); //返回lx
}
public static void change(People people){//将people传递给了change方法
people.name = "lx"; // change方法中people.name是有值的,指向的是People类中的name
}
}
class People{
String name;
}
4.3 方法重载
一个类中,两个方法的名字相同,但两个方法的==参数(形参)==不同。
- 重载的规则:
- 方法名称相同;
- 参数列表必须不同(个数不同,类型不同,参数排列顺序不同等);
- 方法的返回类型可以相同也可以不同;
- 仅仅返回类型不同不足以成为方法的重载。
- 实现理论:
方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,以选择对应的方法。
- 方法重载练习
public class Demo03 {
public static void main(String[] args) {
int sum = add(1,2);
System.out.println(sum);
}
public static int add(int a, int b){
return a+b;
}
public static int add(int a, int b, int c){
return a+b+c;
}
}
运行结果:
3 //因为调用的时候,传了2个参数,匹配的是上面的add方法
4.4 命令行传参
在DOS窗口直接运行一个class文件,需要找到包的路径,在该包的根目录下运行class文件
4.5 可变参数
Java支持传递同类型的可变参数给一个方法
在方法声明中,在指定参数类型后加一个省略号(…)
一个方法中只能指定一个可变参数,必须放在所有参数的最后
public class Demo04 {
public static void main(String[] args) {
Demo04 demo04 = new Demo04();
demo04.test(1,2,3,4); //给可变参数i传递实参
}
public void test(int... i){ //这里的i就是可变参数
System.out.println(i[0]);
System.out.println(i[1]);
}
}
运行结果:
1
2
4.6 递归
一个方法调用自己。
递归包含两个部分:
递归头:什么时候不调用自身方法
递归体:什么时候需要调用自身方法
public class Demo05 {
public static void main(String[] args) {
System.out.println(f(3));
}
public static int f(int i){
if (i==1){ //f(1)就是递归的头,递归到f(1)时就停止递归了
return 1;
}else {
return i*f(i-1);
}
}
}
运行结果:
6
五、Java数组
5.1 数组的定义
- 数组是相同数据类型的有序集合;
- 描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成;
- 每个数据称作数组元素,通过数组下标访问。
5.2 数组的声明和创建
- 数组声明的语法
dataType[] arrayRefVar; //首选的方法
或
dataType arrayRefVar[];
- 使用new操作符创建数组
dataType[] arrayRefVar = new dataType[arraySize];
- 获取数组长度
arrays.length
数组练习1
public class Array01 {
public static void main(String[] args) {
int[] nums = new int[5]; //声明并创建数组
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
System.out.println(sum);
}
}
运行结果:
15
5.3 内存分析和三种初始化
内存分析
声明时,数组压入栈中
new创建时,在堆中开辟空间
三种初始化
- 静态初始化:创建+赋值
int[] nums01 = {0,1,2,3};
- 动态初始化:包含默认初始化
int[] nums02 = new int[3];
nums02[0] = 10;
- 默认初始化
数组被分配空间后,每个元素也按照实例变量同样的方式被隐式初始化。
5.4 数组的使用
- 练习1
普通的for循环
public class Array03 {
public static void main(String[] args) {
int[] nums = {1,2,3,4,5};
int max = nums[0];
for (int i = 0; i < nums.length; i++) {
if (nums[i] > max){
max = nums[i];
}
System.out.println(nums[i]);
}
System.out.println("max="+max);
}
}
- 练习2
for-Each循环
public class Array04 {
public static void main(String[] args) {
int[] nums = {1,2,3,4};
//没有下标,没办法操作数组元素
for (int num : nums) {
System.out.print(num);
}
}
}
运行结果;
1234
- 练习3
数组作方法入参
public class Array04 {
public static void main(String[] args) {
int[] nums = {1,2,3,4};
print(nums);
}
public static void print(int[] arrays){
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
}
}
运行结果;
1 2 3 4
- 练习4
数组作为返回结果
public class Array04 {
public static void main(String[] args) {
int[] nums = {1,2,3,4,5};
int[] reverse = reverse(nums);
printArray(reverse);
}
public static int[] reverse(int[] arrays){
int[] result = new int[arrays.length];
for (int i = 0, j = arrays.length - 1; i < arrays.length; i++,j--) {
result[i] = arrays[j];
}
return result;
}
public static void printArray(int[] arrays){
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
}
}
运行结果:
5 4 3 2 1
5.5 多维数组
多维数组可以看成是数组的数组
int a[][] = new int[2][5];
- 多维数组的使用
public class Array05 {
public static void main(String[] args) {
int[][] array = {{1,2},{2,3},{3,4},{4,5}};
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.print(array[i][j]+" ");
}
}
}
}
5.6 Arrays类
- 数组的工具类java.util.Arrays
- Arrays类中的方法都是static修饰的静态方法,使用的时候可以直接使用类名进行调用
import java.util.Arrays;
public class Array06 {
public static void main(String[] args) {
int[] a = {1,22,4,55,783,4536,78};
Arrays.sort(a); //对数组进行排序:升序
System.out.print(Arrays.toString(a));
}
}
运行结果:
[1, 4, 22, 55, 78, 783, 4536]
5.7 冒泡排序
import java.util.Arrays;
public class Array07 {
public static void main(String[] args) {
int[] a = {1,22,4,55,783,4536,78};
int[] sort = sort(a);
System.out.print(Arrays.toString(sort));
}
//冒泡排序(升序)
public static int[] sort(int[] arrays){
int temp = 0;
boolean flag = false;
//外层循环,判断比较的次数
for (int i = 0; i < arrays.length-1; i++) {
//内层循环,将两个相邻数中较小的一个放到前面(设置一个临时变量,需要交换位置则将小的数放入临时变量)
for (int j = 0; j < arrays.length-1-i; j++) {
if (arrays[j+1] < arrays[j]){
temp = arrays[j];
arrays[j] = arrays[j+1];
arrays[j+1] = temp;
flag = true;
}
}
if (flag==false){
break;
}
}
return arrays;
}
}
5.8 稀疏数组
- 二维数组中的许多元素值默认是0,不需要特别做记录,所以用稀疏数组解决。
- 如果一个数组中大部分数字都是0或者都是相同的数字,就可以用稀疏数组来保存该
- 稀疏数组的处理方式是:
- 记录数组有几行几列,有多少个不同值;
- 把具有不同值的元素的行、列和元素值保存在一个小规模数组中。
六、面向对象编程OOP
6.1 面向对象
- 面向过程
步骤清晰简单,第一步做什么,第二部做什么
- 面向对象
分类的思维,将问题分类后,对这些分类进行单独的思考
- 本质
以类的方式组织代码,以对象的形式组织(封装)数据
- 三大特性
- 封装:包装数据
- 继承:类之间
- 多态:
- 对象,是具体的事物,是类的具体实例。
- 类,是对某一类事物的整体描述,是对象的抽象
6.2 对象的创建分析
使用new关键字创建对象
6.2.1 理解对象练习
public class Application {
public static void main(String[] args) {
//类实例化之后会返回一个自己的对象
//lx、py对象就是Student类的一个具体实例
//使用new关键字创建对象的时候,除了会给对象分配内存空间外,还会进行默认初始化,以及对类中构造器的调用。
Student lx = new Student(); //lx和py是两个不同的对象
Student py = new Student();
lx.name = "lx";
py.age = 18;
System.out.println(lx.name);
System.out.println(lx.age);
}
}
运行结果:
lx
18
public class Student {
//属性:字段
String name; //默认为null
int age; //默认为0
//方法
public void study(){
System.out.println(this.name + "在学习java");
}
}
一个类中,只能有属性和方法
6.2.2 构造器
- 类中的构造器也称为构造方法,是在进行创建对象的时候必须调用的,构造器的特点:
- 必须和类的名字相同;
- 必须没有返回类型,也不能写void。
- 构造器的作用:
- 使用new关键字,本质是在调用构造器
- 用来初始化对象的值
- 注意点
- 定义了有参构造后,如果想使用无参构造,需要显示定义一个无参构造
- 无参构造在默认存在的
public class Application {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.name);
}
}
public class Student {
String name;
//无参构造,显示定义(显示定义是指,在代码中将这行代码写出来)
public Student() {
}
//有参构造,只要定义有参构造,无参就必须显示定义
public Student(String name) {
this.name = name;
}
}
快捷键:Alt + Insert
6.2.3 创建对象内存分析
import com.oop.demo03.Pet;
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "阿福";
dog.age = 1;
dog.bark();
System.out.println(dog.name);
System.out.println(dog.age);
Pet cat = new Pet();
}
}
package com.oop.demo03;
public class Pet {
public String name;
public int age;
public void bark(){
System.out.println("哈哈哈");
}
}
堆中有一个方法区用来加载类和方法以及其中的,在运行的时候,栈中有main方法,new一个对象之后,栈中会生成一个引用变量,这个引用变量指向堆中具体的对象,不同的对象具有不同的地址。
6.3 面向对象三大特征
6.3.1 封装
- 程序要求”高内聚,低耦合“
- 封装(数据的隐藏)
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这种方式称为信息隐藏。 - 属性私有,get/set
- insert + Ins快捷键插入get和set
封装代码说明
package com.oop.Demo04;
public class Student {
private String name; //属性私有
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>120 || age<0){
System.out.print("输入年龄不合法"+"\t");
this.age = 3;
}else {
this.age = age;
}
}
}
import com.oop.Demo04.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.setName("lxpy");
System.out.println(student.getName());
student.setAge(1220);
System.out.println(student.getAge());
}
}
6.3.2 继承
- 继承的本质是对某一批类的抽象
- 子类继承父类,使用extends关键字,子类(派生类)是父类(基类)的扩展
- Java中只有单继承,没有多继承
- 在Java中,所有的类都默认继承Object类
- 继承是类和类之间的一种关系,此外,类之间的关系还有依赖、组合、聚合等。
- 快捷键:Ctrl + H,查看类间的继承关系
- 修饰符和继承的关系:
- 继承练习1
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();
}
}
public class Person {
public void say(){
System.out.println("你好");
}
}
//子类继承父类,就会拥有父类的全部非私有成员
//private修饰的只能被本类的非静态的属性、方法使用。即使被继承,由于作用域的原因,依然无法被使用
public class Student extends Person {
}
super关键字
- 继承练习2
import com.oop.Demo05.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.hello("py");
}
}
运行结果:
py
liuxin
lx
package com.oop.Demo05;
public class Student extends Person {
private String name = "liuxin";
public void hello(String name){
System.out.println(name); //这里的name为形参,在Application中赋值为py
System.out.println(this.name); //this.name是Student类中的name
System.out.println(super.name); //super.name是父类Person中的name
}
}
package com.oop.Demo05;
public class Person {
protected String name = "lx";
}
- 继承练习3
import com.oop.Demo05.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
}
}
运行结果:
Person无参执行
Student无参执行
package com.oop.Demo05;
public class Student extends Person {
public Student() {
//隐藏代码super():默认调父类的无参构造
super(); //调用父类的无参构造,必须放在子类构造器的第一行
System.out.println("Student无参执行");
}
}
package com.oop.Demo05;
public class Person {
public Person() {
System.out.println("Person无参执行");
}
}
- 注意点
1. super调用父类的构造方法,必须在构造方法的第一行
2. super必须只能出现在子类的方法或构造方法中
3. super和this不能同时调用构造方法(这两个关键字必须放在构造方法的第一行,同时调用会报错)
- 和this的比较
1. 代表的对象不同
this:本身调用者这个对象
super:代表父类对象的应用
2. 前提
this:没有继承也可以使用
super:只能在继承条件下才可以使用
3. 构造方法
this:本类的构造
super:父类的构造
重写
- 方法的调用只和等号左边定义的数据类型有关
import com.oop.Demo05.A;
import com.oop.Demo05.B;
public class Application {
public static void main(String[] args) {
B b = new B(); //数据类型是B
b.test();
//父类的引用指向了子类
A a = new B(); //数据类型是A
a.test();
}
}
运行结果:
B的方法实现了
A的方法实现了
public class B extends A{
//这里加了static关键字
public static void test() {
System.out.println("B的方法实现了");
}
}
public class A {
public static void test(){
System.out.println("A的方法实现了");
}
}
- 方法的重写
- 重写都是方法的,和属性无关
- 重写只和非静态方法有关,和静态方法无关
import com.oop.Demo05.A;
import com.oop.Demo05.B;
public class Application {
public static void main(String[] args) {
B b = new B();
b.test();
A a = new B(); //子类重写了父类的方法,所以调用的都是B中的方法
a.test();
}
}
运行结果:
B的方法实现了
B的方法实现了
public class B extends A{
//Override 重写
@Override //注解:有功能的注释
public void test() {
System.out.println("B的方法实现了");
}
}
public class A {
public void test(){
System.out.println("A的方法实现了");
}
}
- 总结
重写:需要有继承关系,子类重写父类的方法
1.方法名必须相同,但方法体不同
2.参数列表必须相同(不同的话会变成重载)
3.修饰符:范围可以扩大但不能缩小
public > protected > default > private
4.抛出的异常:范围可以缩小但不能扩大
快捷键:Alt + Insert,选择override
6.3.3 多态
- 同一个方法可以根据发送对象的不同而采用多种不同的行为方式
- 一个对象的实际类型是确定的,但可以指向对象的引用类型有很多(一般是父类或者是有关系的类)
import com.oop.demo06.Person;
import com.oop.demo06.Student;
public class Application {
public static void main(String[] args) {
//一个对象的实际类型是确定的,这里对象的类型都是Student
//但指向的引用类型就不确定了,父类的引用指向子类
//Student能调用的方法都是自己的或继承父类的
Student a = new Student();
//Person父类,可以指向子类,但不能调用子类独有的方法
Person b = new Student();
((Student)b).test1();
a.test();
}
}
运行结果:
子类方法实现
重写的方法实现
public class Student extends Person{
@Override
public void test() {
System.out.println("重写的方法实现");
}
public void test1(){
System.out.println("子类方法实现");
}
}
public class Person {
public void test(){
System.out.println("父类方法实现");
}
}
- 多态的注意事项
1. 多态是方法的多态,属性没有多态
2. 在转换类型的时候,需要有父子关系,否则会报ClassCastException的错误
3. 存在的条件:继承关系、方法需要重写、父类的引用指向子类对象
Father a = new Son()
instanceof 类型转换
可以判断两个类之间是否存在父子关系,如果不存在关系,则编译不通过
//Student是Person的子类
public class Application {
public static void main(String[] args) {
//Object > Person > Student
Object object = new Student();
System.out.println(object instanceof Student);
System.out.println(object instanceof Person);
System.out.println(object instanceof Object);
System.out.println(object instanceof String);
Student student = new Student();
System.out.println(student instanceof Student);
System.out.println(student instanceof Person);
System.out.println(student instanceof Object);
}
}
运行结果:
true
true
true
false
true
true
true
- 类型转换
- 子类转换成父类,向上转型,但可能会丢失自己本来的一些方法
- 父类转换成子类,向下转型,需要强制转换
例:Object是Student的父类,可以有Object obj = new Student;
//Student是Object的子类,Student类中有一个go方法
public class Application {
public static void main(String[] args) {
//类型之间的转换
Object obj = new Student();
//obj对象转换为Student类型,就可以使用Student类型的方法了
((Student)obj).go();
}
}
6.4 抽象类
- abstract修饰符修饰的类、方法就是抽象类和抽象方法
- 抽象类中可以没有抽象方法,但有抽象方法一定要声明抽象类
- 抽象类,不能用new关键字创建对象,它是用来让子类继承的
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的
- 子类继承抽象类,必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
public abstract class Action {
//abstract约束,其他地方实现这个方法
public abstract void doSomething();
}
//A继承了Action抽象类,就要实现Action中没有实现的抽象方法
public class A extends Action{
@Override
public void doSomething() {
}
}
6.5 内部类
内部类就是在一个类的内部定义一个类
- 外部类、内部类
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); //通过外部类来实例化内部类
inner.in();
inner.getID();
inner.getOutt();
}
}
public class Outer {
private int id = 1;
private void outt(){
System.out.println("外部类的方法");
}
public class Inner{
public void in(){
System.out.println("内部类的方法");
}
//获得外部类的私有属性
public void getID(){
System.out.println(id);
}
//获得外部类的私有方法
public void getOutt(){
outt();
}
}
}
- 另一种内部类
public class Outer {
}
//一个java类中最多只能有一个public class,但可以有多个class
class A{
public static void main(String[] args) {
}
}
- 局部内部类
public class Outer {
public void method(){
class Inner{ //写在方法内部的类
public void write(){ //局部内部类也可以写方法
}
}
}
}
- 匿名内部类
public class Outer {
public static void main(String[] args) {
new Fruit().apple(); //实例化之后没有引用变量名
}
}
class Fruit{
public void apple(){
System.out.println("苹果");
}
}
七、Java接口的定义与实现
通过interface声明一个接口,在接口中定义方法,然后在实现类使用implements实现接口,最后在实现类中重写接口中定义的方法(方法的实现)
- 普通类:只有具体的实现
- 抽象类:具体实现和规范(抽象方法)都有
- 接口:只有规范,自己无法写方法,约束和实现分离
7.1 接口的定义
- 声明类的关键字是class,声明接口的关键字是interface,接口都需要实现类
- 实现类中有方法的实现,接口中只有方法的定义
public interface UserService { //使用interface声明接口
//接口中的所有属性都是常量public static final
int age = 99;
//接口中定义的所有方法都是抽象的public abstract
void add(String name); //可以省去public abstract
void delete(String name);
}
7.2 接口实现类的格式:
//一般接口的实现类的命名,在接口的后面加Impl
//实现类的定义:implements 接口名
//实现了接口的类,就需要重写接口中的方法
public class UserServiceImpl implements UserService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
}
7.3 多个接口在同一个实现类中
- 声明接口
public interface TimeService {
void timer();
}
public interface UserService {
void add(String name);
void delete(String name);
}
- 实现多个接口
//Java通过接口实现多继承
public class UserServiceImpl implements UserService, TimeService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void timer() {
}
}
7.4 总结
- 接口有约束
- 接口中定义一些方法
- 接口不能被实例化,因为接口中没有构造方法,只是定义了方法
- implements可以实现多个接口
- 实现接口必须重写其中的方法
八、异常机制
三种异常
- 检查性异常
用户错误或问题引起的异常,容易被程序员忽视
- 运行时异常
这种异常容易被发现,因为写代码的人知道程序是如何运行的
- 错误ERROR
错误不是异常,是脱离程序员控制的问题
8.1 异常体系架构
- Java把异常当做对象来处理,并定义了一个基类java.lang.Throwable作为所有异常的超类
- 在Java API中定义了许多异常类,分为两大类,错误ERROR和Exception
- ERROR类对象由JVM生成并抛出,大多数错误与代码编写者所执行的操作无关
- Exception中有一个子类RuntimeException(运行时异常)
- ArrayIndexOutOfBoundsException 数组下标越界
- NullPointerException 空指针异常
- ArithmeticException 算术异常
- MissingResourceException 丢失资源
- ClassNotFoundException 找不到类
这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理
这些异常一般是写代码的时候逻辑出错
8.2 Java异常处理机制
异常处理的关键字:
try、catch、finally、rhrow、throws
public class Demo02 {
public static void main(String[] args) {
int a = 1;
int b = 0;
//假设要捕获多个异常,异常的类型要从小到大
try { //try监控区域
System.out.println(a/b);
}catch (Error e){ //catch捕获想要捕获的异常类型
System.out.println("Error"); //捕获异常后的解决方法
}catch (Exception e){
System.out.println("Exception");
}catch (Throwable t){
System.out.println("Throwable");
}finally { //可以没有finally,一般用来结束IO、资源
System.out.println("finally");
}
}
}
public class Demo02 {
public static void main(String[] args) {
try {
new Demo02().test(10,0);
} catch (ArithmeticException e) {
e.printStackTrace(); //捕获异常后,在catch中解决这个异常
}
}
//假设这个方法中处理不了这个异常,方法在上抛出,用throws
//在一个地方抛出异常就好
public void test (int a,int b) throws ArithmeticException{
if (b==0){
throw new ArithmeticException(); //主动抛出的异常,一般用在方法中
}
System.out.println(a/b);
}
}
8.3 自定义异常
自定义异常类,继承Exception类即可
自定义异常类的步骤:
- 创建自定义异常类;
- 在方法中通过throw关键字抛出异常对象;
- 如果在当前抛出异常的方法中处理异常,可以使用try-catch捕获并处理;否则在方法的声明处通过throws关键字指明要抛出的给方法调用者的异常;
- 在出现异常方法的调用者中捕获并处理异常。
8.4 总结
- 处理运行时异常时,采用逻辑去合理规避的同时使用try-catch辅助处理
- 在多重catch块后,可以加一个catch(Exception)来处理可能被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量在catch中处理异常,而不是简单的调用printStackTrace()去打印输出
- 尽量添加finally语句块去释放占用的资源
以上内容是学习Java的一些总结笔记,后续会更新MyBatis、Spring、Spring MVC、Spring Boot等内容的学习笔记。