今天查看了一下我的CSDN主页,发现今天恰好是我加入CSDN一周年的日子。
回想大一的时候,东学学西看看,都只学了一些皮毛。现在大二了,必须要踏实下来深耕了。
今天来刷JavaSE的方法和数组,视频看起来,代码敲起来,博客写起来!
文章目录
Java方法详解
方法概述
System.out.println();
这行代码的意思是调用系统类里的标准输出对象out中的println()方法
Java方法是语句的集合,它们一起执行一个功能。
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样就利于我们后期的扩展
回顾:方法的命名规则?
首字母小写和小驼峰原则 run(),runRun()
public class Method {
//main方法
public static void main(String[] args) {
int c=add(1,2);
System.out.println(c); //3
}
//加法
public static int add(int a,int b){
return a+b;
}
}
方法的定义
Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含以下语法:
方法包含一个方法头和一个方法体。下面是一个方法的所有部分:
- 修饰符:修饰符是可选的,它告诉编译器如何调用该方法,定义了该方法的访问类型
- 返回值类型:方法可能会返回值。returnValueType是方法返回值的数据类型。有些方法执行所需的操作但没有返回值,在这种情况下returnValueType是关键字void
- 方法名:是方法的实际名称。方法名和参数表共同构成方法签名
- 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数
- 形式参数:在方法被调用时用于接受外界输入的数据
- 实参:调用方法时实际传给方法的数据
- 方法体:方法体包含具体的语句,定义该方法的功能
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
方法的调用
调用方法:对象名.方法名(实参列表)
Java支持两种调用方法的方式,根据方法是否返回值来选择
当方法返回一个值的时候,方法调用通常被当做一个值。例如:
int larger=max(30,40);
如果方法返回值是void,方法调用一定是一条语句
System.out.println("Hello,World!");
方法的重载
重载就是在一个类中,有相同的函数名称,但形参不同的函数。
方法的重载规则:
- 方法名称必须相同
- 参数列表必须不同(个数不同或类型不同或参数排列顺序不同等)
- 方法的返回类型可以相同也可以不同
- 仅仅返回类型不同不足以成为方法的重载
实现理论:
方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错
public class Method {
public static void main(String[] args) {
int max=max(10,10);
System.out.println(max);
double max_02=max(10.0,9.8);
System.out.println(max_02);
}
public static int max(int a,int b){
int result=0;
if(a==b){
System.out.println("a==b");
return result;
}
if(a>b){
result=a;
}else{
result=b;
}
return result;
}
public static double max(double a,double b){
double result=0.0;
if(a==b){
System.out.println("a==b");
return result;
}
if(a>b){
result=a;
}else{
result=b;
}
return result;
}
}
a==b
0
10.0
命令行传参
有时候你希望运行一个程序的时候再传递给它消息。这要靠传递命令行参数给main()函数实现。
package com.day03;
public class CommandLine {
public static void main(String[] args) {
//args.length 数组长度
for(int i=0;i<args.length;i++){
System.out.println("args["+i+"]:"+args[i]);
}
}
}
我们打开命令行,执行下面的操作
当产生错误:找不到或无法加载主类 CommandLine时,我们返回到src文件夹下,再执行java com.day03.CommandLine,就不会报错。
第一次执行java com.day03.CommandLine时没有产生任何输出,这是因为我们没有进行传参。所有第二次我们通过命令行传参得到了预期的结果。
可变参数
我们写方法时可能遇到方法有很多不确定的参数的情况,这时候过多的重载会导致代码很繁琐,我们可以用可变参数(不定项参数)解决这个问题。
JDK1.5开始,Java支持传递同类型的可变参数给一个方法。
在方法声明中,在指定参数类型后加一个省略号(…)。
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
public class Demo {
public static void main(String[] args) {
printMax(34,3,56,78,99,123,56.6,7.8);
//可变长参数的本质还是数组
printMax(new double[]{1,2,3});
}
public static void printMax(double... numbers){
if(numbers.length==0){
System.out.println("No argument passed!");
return;
}
double result=numbers[0];
//找出最大值
for(int i=1;i<numbers.length;i++){
if(numbers[i]>result){
result=numbers[i];
}
}
System.out.println("The Max value is "+result);
}
}
The Max value is 123.0
The Max value is 3.0
递归
A方法调用B方法,这很容易理解。
递归就是:A方法调用A方法自己
利用递归可以用简单的程序解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
递归结构包括两个部分:
- 递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
我喜欢把它叫做递归出口
- 递归体:什么时候需要调用自身方法。
如果想搞清楚递归的问题,我们至少需要单独再写一篇博客。这里只举几个简单的例子并附上一些博客或网址的链接。
阶乘
public class Demo {
public static void main(String[] args) {
int res=factorial(5);
System.out.println(res);
}
public static int factorial(int n){
if(n==1) return 1;
return n*factorial(n-1);
}
}
120
注意:当递归层数过多,深度过大时,有可能发生栈溢出(stackoverflow)的问题
参考博客:
练习:计算器
import java.util.Scanner;
public class Calculator {
public static void main(String[] args) {
double num1=0.0,num2=0.0;
double result=0.0;
Scanner scanner=new Scanner(System.in);
System.out.println("请输入数字:");
do{
num1=scanner.nextDouble();
System.out.println("请输入运算符:");
String sign=scanner.next();
System.out.println("请输入数字:");
if(scanner.hasNextDouble()){
num2=scanner.nextDouble();
}
switch(sign){
case "+":
result=add(num1,num2);
System.out.println(num1+"+"+num2+"'s result is:"+result);
break;
case "-":
result=sub(num1,num2);
System.out.println(num1+"-"+num2+"'s result is:"+result);
break;
case "*":
result=multi(num1,num2);
System.out.println(num1+"*"+num2+"'s result is:"+result);
break;
case "/":
if(num2==0){
System.out.println("除数不能为0!");
break;
}
result=div(num1,num2);
System.out.println(num1+"/"+num2+"'s result is:"+result);
break;
default:
System.out.println("Wrong input!");
}
System.out.println("请输入数字:(输入任意字母退出)");
}while(scanner.hasNextDouble());
System.out.println("感谢使用!再见!");
}
public static double add(double a,double b){
return a+b;
}
public static double sub(double a,double b){
return a-b;
}
public static double multi(double a,double b){
return a*b;
}
public static double div(double a,double b){
return a/b;
}
}
请输入数字:
5
请输入运算符:
/
请输入数字:
0
除数不能为0!
请输入数字:(输入任意字母退出)
3
请输入运算符:
/
请输入数字:
4
3.0/4.0's result is:0.75
请输入数字:(输入任意字母退出)
a
感谢使用!再见!
我们在日常使用手机上的计算器时,一般喜欢计算出结果后再用这个结果当作第一个操作数,然后再按运算符号和数字键进行下一次运算。
下面的代码就是针对这种情况的:
import java.util.Scanner;
public class Calculator {
public static void main(String[] args) {
double num1=0.0,num2=0.0;
double result=0.0;
double total=0.0;
Scanner scanner=new Scanner(System.in);
System.out.println("请输入数字:");
num1=scanner.nextDouble();
System.out.println("请输入运算符:");
String sign=scanner.next();
System.out.println("请输入数字:");
if(scanner.hasNextDouble()){
num2=scanner.nextDouble();
}
switch(sign){
case "+":
result=add(num1,num2);
System.out.println(num1+"+"+num2+"'s result is:"+result);
break;
case "-":
result=sub(num1,num2);
System.out.println(num1+"-"+num2+"'s result is:"+result);
break;
case "*":
result=multi(num1,num2);
System.out.println(num1+"*"+num2+"'s result is:"+result);
break;
case "/":
if(num2==0){
System.out.println("除数不能为0!");
break;
}
result=div(num1,num2);
System.out.println(num1+"/"+num2+"'s result is:"+result);
break;
default:
System.out.println("Wrong input!");
}
total=result;
System.out.println("请输入运算符:");
Scanner scanner1=new Scanner(System.in);
double num=0.0;
do{
String str=scanner1.next();
System.out.println("请输入数字:");
num=scanner1.nextDouble();
switch (str){
case "+":
System.out.print(total+"+"+num+"'s result is:");
total=add(total,num);
System.out.println(total);
break;
case "-":
System.out.print(total+"-"+num+"'s result is:");
total=sub(total,num);
System.out.println(total);
break;
case "*":
System.out.print(total+"*"+num+"'s result is:");
total=multi(total,num);
System.out.println(total);
break;
case "/":
System.out.print(total+"/"+num+"'s result is:");
total=div(total,num);
System.out.println(total);
break;
}
System.out.println("请输入运算符:(输入任意数字退出)");
}while((!scanner1.hasNextDouble())&&(!scanner1.hasNextInt()));
System.out.println("感谢使用!再见!");
}
public static double add(double a,double b){
return a+b;
}
public static double sub(double a,double b){
return a-b;
}
public static double multi(double a,double b){
return a*b;
}
public static double div(double a,double b){
return a/b;
}
}
请输入数字:
5
请输入运算符:
+
请输入数字:
6
5.0+6.0's result is:11.0
请输入运算符:
-
请输入数字:
1
11.0-1.0's result is:10.0
请输入运算符:(输入任意数字退出)
+
请输入数字:
4
10.0+4.0's result is:14.0
请输入运算符:(输入任意数字退出)
/
请输入数字:
2
14.0/2.0's result is:7.0
请输入运算符:(输入任意数字退出)
7
感谢使用!再见!
数组
数组的定义
数组是相同类型数据的有序集合。
数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。
其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。
数组的声明和创建
首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
dataType[] arrayRefVar; //首选的方法
dataType arrayRefVar[]; //效果相同,但不是首选方法
Java语言使用new操作符来创建数组,语法如下:
dataType[] arrayRefVar=new dataType[arraySize];
数组的元素是通过索引访问的,数组索引从0开始。
获取数组长度
arrays.length
public class ArrayDemo {
public static void main(String[] args) {
int[] numbers;//声明一个数组
numbers=new int[10];//创建一个数组,这里才为其分配了内存空间
//给数组元素中赋值
for(int i=0;i<numbers.length;i++){
numbers[i]=i+1;
System.out.print(numbers[i]+" ");
}
System.out.print("\n");
int sum=0;
for(int i=0;i<numbers.length;i++){
sum+=numbers[i];
}
System.out.println(sum);
}
}
1 2 3 4 5 6 7 8 9 10
55
内存分析
Java内存分析:
堆:存放new的对象和数组
可以被所有的线程共享,不会存放别的对象引用
栈:存放基本变量类型(会包含这个基本类型的具体数值)
引用对象的变量(会存放这个引用在堆里面的具体地址)
方法区:可以被所有的线程共享
包含了所有的class和static变量
当我们声明数组的时候,堆区内并不存在数组,只有创建时才会为其分配空间。
三种初始化
静态初始化
int[] a={1,2,3};
Man[] mans={new Man(1,1),new Man(2,2)};
动态初始化
int[] a=new int[2];
a[0]=1;
a[1]=2;
数组的默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
public class ArrayDemo {
public static void main(String[] args) {
//静态初始化:创建+赋值
int[] a={1,2,3,4,5,6,7};
System.out.println(a[0]);
//动态初始化:包含默认初始化
int[] b=new int[10];
b[0]=10;
System.out.println(b[0]);
System.out.println(b[1]);
System.out.println(b[2]);
}
}
1
10
0
0
数组的四个基本特点
- 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
- 其元素必须是相同类型,不允许出现混合类型。
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型。
- 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
数组边界
下标的合法区间:[0,length-1],如果越界就会报错
public static void main(String[] args) {
int[] a=new int[2];
System.out.println(a[2]);
}
//java.lang.ArrayIndexOutOfBoundsException 数组下标越界异常
小结
- 数组是相同数据类型(数据类型可以为任意类型)的有序集合
- 数组也是对象。数组元素相当于对象的成员变量
- 数组长度是确定的,不可变的。如果越界,则报:ArrayIndexOutOfBoundsException
数组使用
增强型for循环
public class ArrayDemo {
public static void main(String[] args) {
int[] arrays={1,2,3,4,5,6};
for (int array : arrays) {
System.out.println(array);
}
}
}
1
2
3
4
5
6
数组作方法入参
public class ArrayDemo {
public static void main(String[] args) {
int[] arrays={1,2,3,4,5,6};
printarrays(arrays);
}
//打印数组元素
public static void printarrays(int[] arrays){
for(int i=0;i<arrays.length;i++){
System.out.print(arrays[i]+" ");
}
}
}
1 2 3 4 5 6
数组作为返回值
public class ArrayDemo {
public static void main(String[] args) {
int[] arrays={1,2,3,4,5,6};
int[] results=_reverse(arrays);
printarrays(results);
printarrays(arrays);
reverse(arrays);
printarrays(arrays);
}
//打印数组元素
public static void printarrays(int[] arrays){
for(int i=0;i<arrays.length;i++){
System.out.print(arrays[i]+" ");
}
System.out.println();
}
//修改原数组的反转数组
public static int[] reverse(int[] arrays){
int temp=0;
for(int head=0,tail=arrays.length-1;head<=tail;head++,tail--){
temp=arrays[head];
arrays[head]=arrays[tail];
arrays[tail]=temp;
}
return arrays;
}
//不修改原数组的反转数组
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[j]=arrays[i];
}
return result;
}
}
6 5 4 3 2 1
1 2 3 4 5 6
6 5 4 3 2 1
多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。
二维数组:
int a[][]=new int[2][5];
解析:以上二维数组a可以看成一个两行五列的数组。
public class ArrayDemo {
public static void main(String[] args) {
int[][] array=new int[2][5];
System.out.println("array.length="+array.length);
System.out.println("array[0].length="+array[0].length);
System.out.println(array[0]);
for(int i=0;i<array.length;i++){
for (int j=0;j<array[0].length;j++){
System.out.println("第"+i+"行"+"第"+j+"列的元素是:"+array[i][j]);
}
}
}
}
array.length=2
array[0].length=5
[I@2f7a2457
第0行第0列的元素是:0
第0行第1列的元素是:0
第0行第2列的元素是:0
第0行第3列的元素是:0
第0行第4列的元素是:0
第1行第0列的元素是:0
第1行第1列的元素是:0
第1行第2列的元素是:0
第1行第3列的元素是:0
第1行第4列的元素是:0
Arrays类
数组的工具类java.util.Arrays
数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。
Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而不用使用对象来调用(注意:不用不是不能)
具有以下常用功能:
- 给数组赋值:通过fill方法
- 对数组排序:通过sort方法,按升序
- 比较数组:通过equals方法比较数组中元素值是否相等
- 查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找
import java.util.Arrays;
public class ArrayDemo {
public static void main(String[] args) {
int[] a={1,5,7,9,45,67,88,41,51,15,0};
System.out.println(a);
//打印数组元素
System.out.println(Arrays.toString(a));
//排序数组
Arrays.sort(a);
System.out.println(Arrays.toString(a));
//数组填充
Arrays.fill(a,0);
System.out.println(Arrays.toString(a));
}
}
[I@58ceff1
[1, 5, 7, 9, 45, 67, 88, 41, 51, 15, 0]
[0, 1, 5, 7, 9, 15, 41, 45, 51, 67, 88]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
冒泡排序
冒泡排序无疑是最为出名的排序算法之一(总共有八大排序)
import java.util.Arrays;
public class ArrayDemo {
public static void main(String[] args) {
int[] a={6,3,9,7,45,23,47,89,0,1,2};
bubbleSort(a);
System.out.println(Arrays.toString(a));
}
//冒泡排序
//比较数组中两个相邻元素,如果第一个数比第二个数大,我们将交换他们的位置
//每次比较都会产生一个最大或最小的数字
//下一轮就会少一次比较
public static void bubbleSort(int[] a){
int temp=0;
for(int i=0;i<a.length-1;i++){
//i代表轮数 a.length-1-i是因为每走一轮就将一个数排好了,下一轮比较的时候就减少i个数的比较次数
for(int j=0;j<a.length-1-i;j++){
if(a[j]>a[j+1]){
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
}
}
[0, 1, 2, 3, 6, 7, 9, 23, 45, 47, 89]
我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为O(n2)
优化
如果这个数组是有序的,那我们进行冒泡排序时就不需要再进行比较和排序了。我们通过一个flag标志位来优化冒泡排序。
import java.util.Arrays;
public class ArrayDemo {
public static void main(String[] args) {
int[] a={6,3,9,7,45,23,47,89,0,1,2};
bubbleSort(a);
System.out.println(Arrays.toString(a));
}
public static void bubbleSort(int[] a){
int temp=0;
for(int i=0;i<a.length-1;i++){
boolean flag=false;
for(int j=0;j<a.length-1-i;j++){
if(a[j]>a[j+1]){
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
flag=true;
}
}
if(flag==false){
break;
}
}
}
}
[0, 1, 2, 3, 6, 7, 9, 23, 45, 47, 89]
我们在外层循环中将flag设置为false,如果第一轮内循环中的比较没有对任何元素进行交换,说明整个数组就是有序的,我们直接通过break跳出循环。
稀疏数组
图片来自 狂神说Java
需求:在五子棋游戏中,有存盘退出和续上盘的功能。
分析问题:因为二维数组的很多值是默认值0,因此记录了很多没有意义的数据
解决:稀疏数组
稀疏数组介绍
当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。
稀疏数组的处理方式是:
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
如下图:左边是原始数组,右边是稀疏数组
上面的五子棋盘用稀疏数组记录为
11 | 11 | 2 |
---|---|---|
1 | 2 | 1 |
2 | 3 | 2 |
稀疏数组例子
import java.util.Arrays;
public class ArrayDemo {
public static void main(String[] args) {
//创建一个二维数组 11*11 0:没有棋子 1:黑棋 2:白棋
int[][] array1=new int[11][11];
array1[1][2]=1;
array1[2][3]=2;
//输出原始数组
System.out.println("输出原始数组:");
for(int[] ints:array1){
for(int anInt:ints){
System.out.print(anInt+"\t");
}
System.out.println();
}
//转换为稀疏数组保存
//获取有效值的个数
int sum=0;
for(int i=0;i<array1.length;i++){
for(int j=0;j<array1[0].length;j++){
if(array1[i][j]!=0) sum++;
}
}
System.out.println("有效值的个数:"+sum);
//创建一个稀疏数组的数组
int[][] array2=new int[sum+1][3];
array2[0][0]=11;//行数
array2[0][1]=11;//列数
array2[0][2]=sum;
//遍历二维数组 将非0的值存放在稀疏数组中
int count=0;
for(int i=0;i<array1.length;i++){
for(int j=0;j<array1[0].length;j++){
if(array1[i][j]!=0){
count++;
array2[count][0]=i;
array2[count][1]=j;
array2[count][2]=array1[i][j];
}
}
}
//输出稀疏数组
System.out.println("输出稀疏数组:");
for(int i=0;i<array2.length;i++){
for(int j=0;j<array2[0].length;j++){
System.out.print(array2[i][j]+"\t");
}
System.out.println();
}
//还原稀疏数组
//读取稀疏数组的值
int[][] array3=new int[array2[0][0]][array2[0][1]];
//给其中的元素还原它的值
for(int i=1;i<array2.length;i++){
array3[array2[i][0]][array2[i][1]]=array2[i][2];
}
//打印
System.out.println("还原的数组:");
for(int[] ints:array3){
for(int anInt:ints){
System.out.print(anInt+"\t");
}
System.out.println();
}
}
}
输出原始数组:
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
有效值的个数:2
输出稀疏数组:
11 11 2
1 2 1
2 3 2
还原的数组:
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0