目录
(1)例子: 设计一个方法用来画星星,几行不确定,直角三角形(方向不确定)
四、面向对象之方法设计练习(主要是练习什么时候需要参数、返回值)
4.3 方法:用来寻找给定的元素是否在数组内存在(Scanner输入一个)
4.7 方法:用来存储给定范围内的素数(2~100)素数在自然数之内
6.3 nextInt() 和 nextLine() 和 next()区别
一、面向对象之属性
1.1 面向过程的编程思想
1、解决问题的时候按照一定的过程(流程) 就好比钟点工:大象装冰箱 总共分几步(1.开门 2.大象装里面 3.关门)以过程为本增加了很多冗余
1.2 面向对象的编程思想
1、解决问题的时候按照现实生活中的规律来考虑问题,考虑在这个问题的过程中,有几个实体参与进来;
2、理解:实体动作(动作的支配者)没有实体动作就发生不了;
3、人、冰箱、大象就是单独的实体(分析每一类个体都有什么特点 做了哪些事情有什么特点)
1.3 类和对象
1、类 ---- 人类;类应该是:抽象笼统的概念,描述一类事物,肯定是具有相同的特征行为。
2、对象 ---- 具体的人;对象应该是:具体的一个事物,人类中的一个具体的人,这个具体的人就是对象,比如我:曹某;
(1)类和对象的内存分配机制
1、变量在栈内存,对象在堆内存;
(2)形参和实参的内存分配理解
1、形参可以理解为是方法执行时的临时变量空间
2、实参可以理解为时方法调用时传递进去的参数
3、如果内容是基本类型 传递的 是值 形参改变 实参不变
4、如果内容是引用类型 传递的 是引用 形参改变 实参跟着改变
1.4名命规范
二、面向对象之设计方法
2.1 方法基本语法
权限修饰符 [特征修饰符] 返回值类型 方法名字( 参数列表 )[抛出的异常]{
方法体
}
其中[特征修饰符]和[抛出的异常]不是必须有的。
2.2 方法、返回值、参数
(1)方法:其实方法就是来描述一件事情的,实际意义上方法也就是在做一件你要做的事。 (2)返回值:是这件事情做完了,留下的一个结果(只有一个)
(3)参数:是做事情之前必须提供的条件(可以多个)
(1)例子: 设计一个方法用来画星星,几行不确定,直角三角形(方向不确定)
public class TestStar {
public static void main(String[] args) {
Star Star = new Star();
Star.drawStar(4,true); //左三角
Star.drawStar(4,false); //右三角
}
}
//boolean是true是左三角(无空格),false是右三角(有空格)
public class Star {
public void drawStar(int line, boolean f) {
if (f) { //没有空格
for (int i = 1; i <= line; i++) {
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
} else { //有空格
for (int i = 1; i <= line; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(" ");
}
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
}
(2)优化:如何优化你的代码,让程序更快
//发现无空格的和有空格的就差下面这个for循环
for (int j = 1; j <= i; j++) {
System.out.print(" ");
}
//这时我们找到最全的代码,其余全部删除
public void drawStar(int line, boolean f) {
for (int i = 1; i <= line; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(" ");
}
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
}
//发现这个有空格的for有时需要,有时不需要,看你是true还是false
//这时就可以通过if去控制它
public class Star {
public void drawStar(int line, boolean f) {
for (int i = 0; i < line; i++) {
//如果true就不执行空格,如果false就执行空格
if(!f) {
for (int j = 1; j <= i; j++) {
System.out.print(" ");
}
}
//无论左三角还是右三角都必须执行这个for循环
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
三、面向对象之方法参数返回值问题
3.1 为什么需要参数、返回值
1、咱们用一个例子来看,为什么方法需要返回值,为什么需要定义参数
2、例子:设计一个方法:用来交换两个数组中的各各元素
3、仔细想一想一个方法需要具备什么:1、修饰符;2、返回值;3、方法名;4、参数
3.2 设计方法:用来交换两个数组元素
public class Demo {
/*
* 设计方法:用来交换两个数组元素
* 1.先有两个数组
* 2.交换数组中元素
* (1)方式一 将两个数组内的元素对应位置互换(用循环方式交换,性能变慢,而且还需要保证两个数组长度一致)
* (2)方式二 用传递地址的形式传递,将两个数组的地址引用直接互换 但是发现并没有交换,这时就用到了返回值
* 3.验证(不是必须要做的)
* */
public void changeTwoArray(int a[], int b[]) {
//2.交换数组中元素
//(1)方式一 将两个数组内的元素对应位置互换
// for (int i = 0; i < a.length; i++) {
// int x = a[i];
// a[i] = b[i];
// b[i] = x;
// }
//(2)方式二 用传递地址的形式传递,将两个数组的地址引用直接互换 但是发现并没有交换(这时就用到了返回值)
int[] temp = a;
a = b;
b = temp;
}
public static void main(String[] args) {
//创建对象
// Demo demo1 = new Demo();
Demo demo2 = new Demo();
//1.先有两个数组
int[] x = {1, 2, 3, 4};
int[] y = {5, 6, 7, 8};
//2.方式一:调用demo对象中的交换方法
// demo1.changeTwoArray(x, y);
//2.方式二:传递地址,但是发现并没有交换,这时就用到了返回值
demo2.changeTwoArray(x, y);
//3.验证
for (int v : x) {
System.out.println(v);
}
System.out.println("============================");
for (int v : y) {
System.out.println(v);
}
}
}
public class Demo2 {
/*
* 设计方法:用来交换两个数组元素
* 1.先有两个数组
* 2.交换数组中元素
* (2)方式二 用传递地址的形式传递,将两个数组的地址引用直接互换 但是发现并没有交换,这时就用到了返回值
* 3.验证(不是必须要做的)
* */
public int[][] changeTwoArray(int a[], int b[]) {
//2.交换数组中元素
/* (2)方式二 用传递地址的形式传递,将两个数组的地址引用直接互换 但是发现并没有交换,这时就用到了返回值
这时方法临时执行空间,他们3个地址进行交换,a指向y地址,b指向x地址了
①为啥没变?:
因为方法执行是临时空间,你这个地址传递是在临时空间进行交换的,当方法执行完毕后就销毁
了,x和y数组就相当于没有变换(所以地址交换就需要用到返回值)
②咱们定义的方法只有一个返回值,但是咱们做完地址传递后,需要将两个数组都返回,这时该怎么办?
那就要返回这两个数组,那么数组的数组是什么?那就是二维数组
*/
int[] temp = a;
a = b;
b = temp;
int[][] result = {a, b};
return result;
}
public static void main(String[] args) {
//1.先有两个数组
int[] x = {1, 2, 3, 4};
int[] y = {5, 6, 7, 8};
//创建对象
Demo2 demo2 = new Demo2();
//2.方式二:传递地址,但是发现并没有交换,这时就用到了返回值
int[][] value = demo2.changeTwoArray(x, y);
x = value[0];
y = value[1];
//3.验证
for (int v : x) {
System.out.println(v);
}
System.out.println("============================");
for (int v : y) {
System.out.println(v);
}
}
}
1、最终蓝色的是return返回来的值,红色是执行方法是临时开辟的空间
四、面向对象之方法设计练习(主要是练习什么时候需要参数、返回值)
4.1 设计一个方法:用来交换一个数组(头尾互换)
1、补充java定义数组小知识:数组必须先开辟空间后才能赋值
(1)int[] Array = new int[]{1, 2, 3, 4, 5}; 直接赋值
(2)int[] Array = new int[10]; 用于循环遍历
2、当传进去的东西 和 返回值返回的东西 是一致的东西,那么这时就不需要返回值了。(数组中如果地址引用是一个就不用返回值,如果不是就用)
3、例子:用来交换一个数组(头尾互换),就不需要返回值
public class Test {
public static void main(String[] args) {
/*
* 设计一个方法:用来交换数组(头尾互换)
* 0. 创建一个对象
* 1. 定义一个数组
* 2. 数组内部的元素头尾对应互换
* 3. 验证结果
* */
//0.创建一个对象
TestFunctions tF = new TestFunctions();
//1. 定义一个数组
int[] x = new int[]{1, 2, 3, 4, 5};
//2.数组内部的元素头尾对应互换
//利用tf对象调用方法执行操作
int[] result = tF.changeArrayElements(x); // 将x -> array
/*
x将存储的地址传递给临时数组array,这时x和array都指向同一个地址,也就是指向同一片存储区
然后临时方法changeArrayElements(),return返回的是array临时内部空间里面的值,
然后主方法中的 result 接收这个值,也就相当于x、array、result都是指向的同一个存储区,
当传进去的东西 和 返回值返回的东西 是一致的东西,那么这时就不需要返回值了。
*/
//3.验证结果
for (int v : result) {
System.out.println(v); //5 2 3 4 1
}
}
}
public class TestFunctions {
/*
* 设计一个方法:用来交换数组(头尾互换)
* 考虑:是否需要参数及其返回值 需要 提供一个数组
* 是否需要返回值 假设需要 数组当返回值
* 1.定义一个数组
* 2.数组内部的元素头尾对应互换
* 3.验证结果
* */
public int[] changeArrayElements(int[] array) {
//2.数组内部的元素头尾对应互换
for (int i = 0; i < array.length; i++) {
int temp = array[i];
array[i] = array[array.length - 1];
array[array.length - 1] = temp;
}
return array;
}
}
4、进行改写,其实是不需要返回值的;因为是一个数组无论临时空间还是存储区中的堆空间,使用的都是一个东西
public class Test {
public static void main(String[] args) {
TestFunctions tF = new TestFunctions();
int[] x = new int[]{1, 2, 3, 4, 5};
tF.changeArrayElements(x); // 将x -> array
for (int v : x) {
System.out.println(v); //5 2 3 4 1
}
}
}
public class TestFunctions {
public void changeArrayElements(int[] array) {
for (int i = 0; i < array.length; i++) {
int temp = array[i];
array[i] = array[array.length - 1];
array[array.length - 1] = temp;
}
}
}
4.2 方法:用来寻找数组中的最大值或最小值
public class Test {
public static void main(String[] args) {
/*
* 设计一个方法:用来寻找数组中的最大值或最小值
* 考虑 是否需要参数 数组 和 最大或最小
* 是否需要返回值
* 0. 创建一个对象
* 1. 定义一个数组
* 2. 找一个变量(最大或最小)
* 3. 利用遍历数组的方式挨个与max比较
* 4. 将找到的值返回
* */
//0.创建一个对象
Test test = new Test();
//1. 定义一个数组
int[] x = new int[]{0, 2, 5, 7, 8, 9, 12};
//调用寻找最大值和最小值的方法
int result = test.findMaxOrMinNum(x,false); //找最小值
int result2 = test.findMaxOrMinNum(x,true); //找最大值
//验证结果
System.out.println("最小值 = " + result);
System.out.println("最大值 = " + result2);
}
//定义方法:flag == true 返回最大值 flag == false 返回最小值
public int findMaxOrMinNum(int[] array, boolean flag) {
//2. 找一个变量(最大或最小)
int temp = array[0];
//3. 利用遍历数组的方式挨个与max比较
for (int i = 1; i < array.length; i++) {
//找寻最小值
if (!flag && temp > array[i]) {
temp = array[i];
}
//找寻最大值 flag == ture
if (flag && temp < array[i]) {
temp = array[i];
}
}
//4. 将找到的值返回
return temp;
}
}
4.3 方法:用来寻找给定的元素是否在数组内存在(Scanner输入一个)
public class TestFunctions {
/*
* 设计一个方法:用来寻找给定的元素是否在数组内存在(Scanner输入一个)
* 考虑:
* 是否需要参数 需要一个数组和一个元素
* 是否需要返回值 需要告诉你是否找到了
* 用flag控制输出 flag==true找到了 反之没有找到
* 返回值:String ,result控制最后输出的结果
* 考虑:
* 1.然后你会发现下面代码String result = "";中的""这个里面写什么都可以,
* 因为无论什么下面都会给result重写赋值
* 2.所以咱们可以直接给result赋初值就为 "恭喜,您要的值在数组中找到了" ;
* 如果没找到result就重写赋值了,如果找到了就直接返回一开始定义的初始化值
* 3.然后就可以去掉最下面的if(flag)了
* 4.在一看boolean flag = true;就是为了控制最下面的if进行输出结果的,结
* 果最下面的if去掉了,所以boolean flag = true;这个定义就无意义了
* */
public String isExist01(int[] array, int element) {
String result = "";
boolean flag = true;
//循环寻找是否存在
for (int i = 0; i < array.length; i++) {
if (array[i] == element) {
result = "恭喜,您要的值在数组中找到了";
flag = false;
//一旦找到就不在进行寻找了
break;
}
}
if (flag) {
result = "对不起,您要的值在数组中没有!";
}
return result;
}
//优化后的代码
public String isExist02(int[] array, int element) {
String result = "对不起,您要的值在数组中没有!";
//循环寻找是否存在
for (int i = 0; i < array.length; i++) {
if (array[i] == element) {
result = "恭喜,您要的值在数组中找到了";
break;
}
}
return result;
}
public static void main(String[] args) {
//0. 创建一个对象
TestFunctions testFunctions = new TestFunctions();
//1. 创建一个数组
int[] arr = new int[]{1, 2, 4};
//找寻给定元素是否在数组中存在
String result = testFunctions.isExist02(arr, 2);
System.out.println(result);
}
}
4.4 方法:用来合并两个数组
/*
* 设计一个方法:用来合并两个数组
* 考虑:
* 是否需要参数 需要两个数组
* 是否需要返回值 需要返回一个大数组
* */
public int[] mergeArray(int[] a, int[] b) {
//创建数组
int[] newArray = new int[a.length + b.length];
//分别将a和b数组的元素存入新的数组内
for (int i = 0; i < a.length; i++) {
newArray[i] = a[i];
}
//第二个数组在第一个数组之后放
for (int i = 0; i < b.length; i++) {
newArray[a.length + i] = b[i];
}
//将新数组返回
return newArray;
}
4.5 方法:将一个数组按照最大值位置拆分
/*
* 设计一个方法:用来将一个数组按照最大值位置拆分(不包含最大值这个元素)
* 考虑:
* 是否需要参数 需要一个大的数组
* 是否需要返回值 (拆分以后变为两个小数组)需要返回二维数组其他数组中
* 数组是更改不了的,只能将原数组的元素挑选出来,然后放到另一个数组中
* */
public int[][] splitArray(int[] array) { //1 2 3 9 4 5
//寻找最大值索引位置
int max = array[0]; //记录最大值
int index = 0; //记录最大值的索引位置
for (int i = 0; i < array.length; i++) {
if (max < array[i]) {
max = array[i];
index = i;
}
}
//通过找寻到的index判定数组拆分后的前后长度
int[] newa = new int[index]; //小数组长度刚好是你找到最大值的索引位置
//原数组长度 减去 index之前的部分(减去1 2 3),再减去你这个最大值原本占用的空间(9)
//,也就是说将1 2 3 存一个小数组里,在将4 5 存一个小数组里
int[] newb = new int[array.length - index - 1];
//分别将两个数组填满遍历
for (int i = 0; i < newa.length; i++) {
newa[i] = array[i];
}
for (int i = 0; i < newb.length; i++) {
//从原数组的9之后开始找 index + 1,将4 5存入
newb[i] = array[( index + 1 ) + i];
}
//将两个新的小数组一起返回
// int[][] result = {newa, newb};
// return result; // 都可以
return new int[][]{newa, newb};
}
4.6 方法:用来去掉数组0元素
/*
* 设计一个方法:用来去掉数组0元素
* 考虑:
* 是否需要参数 需要一个大的数组 和 要删除的元素是什么
* 是否需要返回值 返回一个没有0的数组(新数组)
* 思想:
* 数组的长度是固定不变的,你想删除空间是删不出去的,所以创建一个小数组,把没有删除的挑出来
* ,剩下的就不要了
* */
//删除数组中的元素
public int[] removeElementFromArray(int[] array, int element) {
//找寻原数组中去掉被删除元素后的长度(因为咱们要知道小数组多长啊)
int count = 0; //用于记录非删除元素的个数
for (int i = 0; i < array.length; i++) {
if (array[i] != element) {
count++;
}
}
//通过找到的count创建一个新数组
int[] newArray = new int[count];
//控制新数组的索引变化
int index = 0;
//将原来数组中非删除的元素存入新数组
for (int i = 0; i < array.length; i++) {//array: 1 0 2
if (array[i] != element) {
//位置不是连续的,如果你给新数组newArray[i],有可能i=1的时候就不进入到这个给if中
//,但是你i还是变化的就不对了,所以你每装一次值我就加一次索引
newArray[index++] = array[i];
}
}
//将新数组返回
return newArray;
}
4.7 方法:用来存储给定范围内的素数(2~100)素数在自然数之内
/*
* 设计一个方法:用来存储给定范围内的素数(2~100)素数在自然数之内
* 考虑:
* 是否需要参数 需要一个素数的范围 起始begin 接收end
* 是否需要返回值 返回一个装满了素数的数组(因为只能return一个东西)
*
* */
public int[] findPrimeNum(int begin, int end) {
if (begin < 0 || end < 0) {
System.out.println("素数没有负数!");
//输入无效的范围,但是又要返回东西,这时就可以返回一个null
// 也可以自定义一个异常(因为你返回null,有时代码多了,你就会认为,为什么定义返回值
// 了,为啥没有给我返回,容易误导,所以这里尽量去返回一个错误的信息)
return null;
}
//起始数大于结束(提供的范围有误)
if (begin >= end) {
System.out.println("您提供的范围有错误!");
return null;
}
//先创建一个足够长的数组,用于存放给定范围内的素数
int[] array = new int[(end - begin) / 2];//因为素数没有偶数全是奇数,所以/2长度就够了
int index = 0; //记录新数组的索引变化,同时记录个数
for (int num = begin; num <= end; num++) {
boolean flag = false;//标记
for (int i = 2; i <= num / 2; i++) {//这个for循环走了一圈发现没有整除的了
if (num % i == 0) { //证明整除了
flag = true;
break;//整除就不是素数
}
}
if (!flag) {
array[index++] = num;
}
}
// 将数组多余的零去掉(去掉多余空间)
int[] primeArray = new int[index];
for (int i = 0; i < primeArray.length; i++) {
primeArray[i] = array[i];
}
//这里要给旧数组指向null,为了节省空间,因为java有垃圾器会将没有用的数组销毁
array = null;
return primeArray;
}
4.8 方法:给数组元素排序(冒泡排序)既能升序也能降序
1、未优化前(主要是思想,解决代码空间和冗余问题)
public class TestFunctions {
/*
* 设计一个方法:给数组进行排序(假设升序)
* 考虑:
* 是否需要参数 需要一个数组 和 一个排序规则boolean
* 是否需要返回值 不需要
* flag==true升序反之降序
* if else 属于但分支语法结构,并且有一样的代码,可以进行优化(无论走哪个分支,代码都会走一编)
* */
public int[] orderArray(int[] array, boolean flag) {
//int[] array = new int[]{2, 3, 5, 1, 4};
//array[4]-------array[3]
//array[3]-------array[2]
//array[2]-------array[1]
//array[1]-------array[0]
//按照标号来看,这是个升序排序(升序 < 降序大于)
if (flag){ //进行升序
//③控制执行轮次,第一轮是第一小的值,第二轮是第二小的值,以此类推(实际意义上就是数组长度)
for (int i = 1; i < array.length; i++) {
//①一共5个数控制比较四次,从最后一个元素比较,发现最后一个元素的索引正好是长度-1
for (int j = array.length - 1; j >= i ; j--) { //未优化j>=1每轮一次就全部比较一次 优化后j>=i每轮一次就少比一次
//②最后一个元素和倒数第二个元素比较
if(array[j] < array[j - 1]) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
}
}
} else { //降序
for (int i = 1; i < array.length; i++) {
for (int j = array.length - 1; j >= i ; j--) {
if(array[j] > array[j - 1]) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
}
}
}
return array;
}
}
public class Test2 {
public static void main(String[] args) {
//0. 创建一个对象
TestFunctions testFunctions = new TestFunctions();
//冒泡排序算法(从数组的低端每次冒出一个值)
int[] x = new int[]{2, 3, 5, 1, 4};
//调用方法进行执行排序
testFunctions.orderArray(x,true);
//加强for循环进行输出array数组
for(int v:x) {
System.out.println(v);
}
}
}
2、优化后
public int[] orderArray(int[] array, boolean flag) {
for (int i = 1; i < array.length; i++) {
for (int j = array.length - 1; j >= i ; j--) {
//什么情况下可以进行元素互换?
//1. 升序:flag==true && array[j] < array[j - 1]
//2. 降序:flag==false && array[j] > array[j - 1]
//所以当(flag==true && array[j] < array[j - 1]) || (flag==false && array[j] > array[j - 1])就可以互换了
if (flag) {//升序
if(array[j] < array[j - 1]) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
}else { //降序
if(array[j] > array[j - 1]) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
}
}
}
return array;
}
3、最终优化版
public int[] orderArray(int[] array, boolean flag) {
for (int i = 1; i < array.length; i++) {
for (int j = array.length - 1; j >= i ; j--) {
//什么情况下可以进行元素互换?
//1. 升序:flag==true && array[j] < array[j - 1]
//2. 降序:flag==false && array[j] > array[j - 1]
//所以(flag==true && array[j] < array[j - 1]) || (flag==false && array[j] > array[j - 1])就可以互换了
if ((flag==true && array[j] < array[j - 1]) || (flag==false && array[j] > array[j - 1])) {//升序
int temp = array[i];
array[j] = array[j - 1];
array[j - 1] = temp;
}
}
}
return array;
}
public class Test2 {
public static void main(String[] args) {
//0. 创建一个对象
TestFunctions testFunctions = new TestFunctions();
//冒泡排序算法(从数组的低端每次冒出一个值)
int[] x = new int[]{2, 3, 5, 1, 4};
//调用方法进行执行排序
testFunctions.orderArray(x,true);
testFunctions.orderArray(x,false);
//加强for循环进行输出array数组
for(int v:x) {
System.out.println(v);
}
}
}
4.9 方法:实现用户登录认证(二维数组当作小数据库)
1、验证思路,我们要进行优化和整改逻辑关系
/*
* 设计方法:实现一个用户的登录认证
* 考虑:
* 需要参数 需要提供账号和密码
* 需要返回值 返回登录成功是否的结果 咱们这里用String返回一串信息
* 1. 需要有小数据库----存储用户真实的账号密码
* 2. 用户输入自己登录得账号和密码
* 3. 提示用户输入账号和密码
* 4. 进行校验
* */
public class TestUser {
//这个数据库就当属性来用,如果把数据库写在方法中,那么每次循环比较都要用一次数据库,属性就不用了
public String[][] userBox = {{"曹宇希", "123456"}, {"五花肉", "88888"}};
}
//4. 进行校验
public String login(String user, String password) {
boolean flag = false; // 标记
for (int i = 0; i < userBox.length; i++) {
//userBox[i][0]肯定是用户名,因为你二维数组创建得就是第一个是用户名
if (userBox[i][0].equals(user)) {
if (userBox[i][1].equals(password)) {
System.out.println("用户登录成功!");
}else {
// ①System.out.println("密码错误!");
System.out.println("用户名或密码错误!");//②
}
flag = true; //找到人名就修改标记
break;
}
}
//当上面循环完毕之后,进行比对之后,才能知道用户名是否存在(防止他人恶意破解最后写为用户名或密码错误)
//上面用户名和密码错误和下面重复了,进行优化,你要知道只有当上面循环走完之后,才能确定用户名在不在,
//所以下面这个if代码肯定不能去掉,所以优化上面的for代码
if (!flag) {
// ①System.out.println("用户名不存在!");
System.out.println("用户名或密码错误!");//②
}
}
2、优化
//4. 进行校验
public String login(String user, String password) {
boolean flag = false; // 标记
for (int i = 0; i < userBox.length; i++) {
//userBox[i][0]肯定是用户名,因为你二维数组创建得就是第一个是用户名
if (userBox[i][0].equals(user)) {
if (userBox[i][1].equals(password)) {
System.out.println("用户登录成功!");
}
flag = true; //找到人名就修改标记
break;
}
}
//当上面循环完毕之后,进行比对之后,才能知道用户名是否存在(防止他人恶意破解最后写为用户名或密码错误)
if (!flag) {
System.out.println("用户名或密码错误!");
}
}
3、优化
//4. 进行校验
public String login(String user, String password) {
boolean flag = false; // 标记
for (int i = 0; i < userBox.length; i++) {
//userBox[i][0]肯定是用户名,因为你二维数组创建得就是第一个是用户名
if (userBox[i][0].equals(user)) {
if (userBox[i][1].equals(password)) {
System.out.println("用户登录成功!");
flag = true; //找到人名就修改标记
}
break;
}
}
//没进入上面的for说明密码或账户错误,就输出!flag
if (!flag) {//单独的flag是不行的因为无论经不经过for循环都不输出
//用户名 或 密码 有一个不正确就需要输出用户名或密码错误(用户和密码全对就不输出)
//代码输出取决于flag标记的值,也就是说用户和密码有一个不正确就不用改flag
//如果flag与最初的一致没有改过就输出(用户和密码有一个错误)
System.out.println("用户名或密码错误!");
}
}
4、最终版:
import java.util.Scanner;
/*
* 设计方法:实现一个用户的登录认证
* 考虑:
* 需要参数 需要提供账号和密码
* 需要返回值 返回登录成功是否的结果 咱们这里用String返回一串信息
* 1. 需要有小数据库----存储用户真实的账号密码
* 2. 用户输入自己登录得账号和密码
* 3. 提示用户输入账号和密码
* 4. 进行校验
* */
public class TestUser {
//这个数据库就当属性来用,如果把数据库写在方法中,那么每次循环比较都要用一次数据库,属性就不用了
public String[][] userBox = {{"曹宇希", "123456"}, {"五花肉", "88888"}};
public static void main(String[] args) {
//创建对象
TestUser testUser = new TestUser();
//实现一个用户登录认证
//2. 用户输入自己登录得账号和密码
Scanner scanner = new Scanner(System.in);
//3. 提示用户输入账号和密码
System.out.println("请输入账号:");
String user = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
//调用方法
String result = testUser.login(user, password);
System.out.println(result);
}
public String login(String user, String password) {
//进行校验
String result = "用户名或密码错误!";
for (int i = 0; i < userBox.length; i++) {
//userBox[i][0]肯定是用户名,因为你二维数组创建得就是第一个是用户名
if (userBox[i][0].equals(user)) {
//userBox[i][1]肯定是密码
if (userBox[i][1].equals(password)) {
result = "用户登录成功!";
}
//密码错误或正确都直接跳出从新验证,密码错误从新输入,密码正确直接进入
break;
}
}
return result;
}
}
4.10 方法:模拟实现计算机
(1)实现思想
1、设计加减乘除四个方法,参数是输入的第一个数和第二个数,返回值float;
2、设计一个实现计算机流程的方法calculate,无参数和返回值
1+1=2
3、第一次输入input.nextLine();然后将输入的数转换为float类型Float.parseFloat(one),不能写在while里面,因为当你想计算第二次结果的时候,第一个次的运算结果会被这行代码重新赋值,从而导致计算结果错误
4、设计一个死循环while(true),当第二次输入是=号的时候break,当输入与计算机无关的验算符合提示输入错误符号"+".equals(symbol);第三次输入,将输入的再次转为float
5、设计switch(输入的符合) case符号: 调用设计好的加减乘除方法;oneValue = this.substact(oneValue,twoValue);
(2)代码实现
import java.util.Scanner;
public class Calculator {
//设计方法:加法运算 需要提供两元素 需要返回计算结果
public float add(float a, float b) {
return a + b;
}
//方法 减法运算
public float substact(float a, float b) {
return a - b;
}
//乘法运算
public float multiply(float a, float b) {
return a*b;
}
//除法
public float divide(float a, float b) {
return a/b;
}
//设计一个方法:用来控制计算机计算的流程
public void calculate() {
Scanner input = new Scanner(System.in);
System.out.println("请输入第一个数字");
String one = input.nextLine();
float oneValue = Float.parseFloat(one); //不能写在while里面,因为当你想计算第二次结果的时候,第一个次的运算结果会被这行代码重新赋值,从而导致计算结果错误
//这个需要一个死循环,计算机输入数然后计算结果后,还可以继续输入数计算结果
while(true) {
System.out.println("请输入符号");
String symbol = input.nextLine();
if ("=".equals(symbol)) { //当输入=就退出
System.out.println("执行完毕");
break;
}
if ( !("+".equals(symbol) || "-".equals(symbol) || "*".equals(symbol) || "/".equals(symbol)) ) {
System.out.println("输入的符号有误,只能是 +、-、*、/、= 中的其中一个");
continue;
}
System.out.println("请输入第二个数字");
String two = input.nextLine();
float twoValue = Float.parseFloat(two);//String--->float 可能出现NumberFormatException
switch (symbol) {
case "+":
//第一次运行完毕的结果需要存起来,当作第二次开始的数字,所以oneValue也应该用来存结果
// ,所以就不能把结果存在变量里了
// float r1 = this.add(oneValue,twoValue);
// System.out.println(r1);
oneValue = this.add(oneValue, twoValue);
break;
case "-":
oneValue = this.substact(oneValue,twoValue);
break;
case "*":
oneValue = this.multiply(oneValue,twoValue);
break;
case "/":
oneValue = this.divide(oneValue,twoValue);
break;
}
//上面的switch无轮最后走到哪个分支都需要输出oneValue结果
System.out.println(oneValue);
}
}
}
public class Test03 {
public static void main(String[] args) {
Calculator calculator = new Calculator();
calculator.calculate();
}
}
五、面向对象之方法重载
5.1 方法重载
1、java 中允许同一个类中,多个同名方法的存在,但要求 形参列表不一致!
2、方法名相同,参数不同
3、参数不同体现在:参数的个数 参数的类型 参数的顺序
(1)调用方法的时候,首先通过方法名字定位方法,如果方法名字有一致,可以通过参数的数据类型定位方法;如果没有与传递参数类型一致的方法,可以找一个参数类型可以进行转化(自动)
5.2 方法重载的动态参数(可变参数)1.5版本以后
1、在JDK1.5版本以后出现了一个新的方法(动态参数)
2、java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
public class VarParameter01 {
//编写一个 main 方法
public static void main(String[] args) {
HspMethod m = new HspMethod();
System.out.println(m.sum(1, 5, 100)); //106
System.out.println(m.sum(1,19)); //20
}
}
class HspMethod {
//1. int... 表示接受的是可变参数,类型是 int ,即可以接收多个 int(0-多)
//2. 使用可变参数时,可以当做数组来使用 即 nums 可以当做数组
//3. 遍历 nums 求和即可
public int sum(int... nums) {
//System.out.println("接收的参数个数=" + nums.length);
int res = 0;
for(int i = 0; i < nums.length; i++) {
res += nums[i];
}
return res;
}
}
3、可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后,就可以通过可变参数实现
4、一个形参列表中只能出现一个可变参数
//细节: 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
public void f2(String str, double... nums) { }
//细节: 一个形参列表中只能出现一个可变参数
//下面的写法是错的.
public void f3(int... nums1, double... nums2) { }
5.2 重载好处
1)减轻了起名的麻烦;2) 减轻了记名的麻烦
六、构造方法 + this用法
6.1 构造方法(构造器)
(1)类的成员
1、利用类来描述现实生活中的事情,利用对象去具体的执行操作
2、类的内部成员(例:Person):
(1)属性 ---- 静态描述类的特征(变量 存值 存数据)name
(2)方法 ---- 动态描述类的行为(做事情)eat
(3)构造方法 ---- 作用是构造当前类的对象(做事情)
(4)程序块{}(可省略) ---- 创建对象后,每运行一次对象,就执行一次(块是在创建对象前就加载完成的)
(2)构造方法的使用
构造方法: 权限修饰符 返回值类型 方法名字( 参数列表 ){ 就为了做一件事情(创建一个对象当前类Person) 返回对象; }
1、构造方法作用是构造当前类的对象,所以构造方法的方法名要与当前类一致;
2、因为构造当前类的对象,所以返回值的类型肯定是当前类,而返回值那就是返回一个对象; (所以我们在写构造方法的时候,就省略返回值类型和返回值,因为这两个是固定不变的)
权限修饰符 方法名字( 参数列表 ){ 方法体; }
3、用法:通过new关键字进行调用 如: Person person = new Person(); 调用Person类中的无参构造器 ,创建对象后,就会默认调用无参构造器。
4、特点: (1)每一个类都有自己的构造方法,若自己在类中没有定义,系统会默认提供一个无参数的构造方法;若在类中定义了构造方法,则默认无参数的构造方法即被覆盖
(2)存在构造方法重载
5、每一个类都有构造方法,若不定义,系统会默认提供构造方法,那为什么要定义构造方法?什么时候需要设计构造方法?
(1)设定构造器就是为了方便初始化;在创建对象的同时,想要一并做一些事情,默认提供的构造方法是不会做的。Person p = new Person("五花肉", 22); 创建对象的同时给对象属性赋值
没有讲构造方法之前:
public class Person{
//属性
public String name;
public int age;
}
public class Test{
public static void main(String[] args) {
Person person= new Person();
person.name = "五花肉";
String na = person.name;
System.out.println(na);
}
}
所以设定构造器就是为了方便初始化:
public class Person{
String name;
int age;
//构造器
public Person(String name, int age) {
//this是指当前调用的对象
this.name = name; //当前对象的属性name值 就等于 你传进来的name变量的值(其实运行机制是后者赋给前者)
this.age = age;
}
}
public class Test{
public static void main(String[] args) {
Person p = new Person("五花肉", 22);
System.out.println(p.name);
}
}
6.2 this用法
1、this 关键字可以用来访问本类的属性、方法、构造器
(1)this 用于区分当前类的属性和局部变量
(2)访问成员方法的语法:this.方法名(参数列表);
(4)访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一条语句)
(5)this 不能在类定义的外部使用,只能在类定义的方法中使用。
6.3 nextInt() 和 nextLine() 和 next()区别
(1)nextInt() ----------- 方法会将回车符做为截至,将回车符之前的所有字符(不读取回车符),但回车符会留在队列中
(2)nextLine() ------ 方法会将回车符做为截至,将回车符连同之前的所有字符都读取出来(读取回车符),将回车符扔掉,把之前的所有字符组合成一个完整的字符串,交给我们
(3)next() 和 nextInt()一样,除了nextLine以外其余的方法都不读取回车符
//这么写会发现name为空,因为nextInt会留下回车符,而nexLine读取回车符,并且把回车去掉,在返回,怎么改变?
//nextInt()在前 nextLine()在后
Scanner scanner = new Scanner(System.in);
int pwd = scanner.nextInt();
String name = scanner.nextLine();
//1、方法一:nextLine()改为next() ,但是最后还是留下了回车符在队列,有可能继续影响下一个东西使用
Scanner scanner = new Scanner(System.in);
int pwd = scanner.nextInt();
String name = scanner.next();
//2、方法二(最节省空间):都用nextLine()读取,因为读取的干净,但是有一个问题密码都是int类型,所以还要数据转换
/*
所以我们将账号和密码都用nexLine()来读取。
这时就要涉及到String --> int,数据转化的问题,类型换行的前提(同种大数据类型一致,也就是基本的和基本的转换,
引用的和引用的转化才可以)
*/
Scanner scanner = new Scanner(System.in);
int pwd = scanner.nextLine();
int value = Integer.parseInt(password); //转化,所以尽量用这种去写,就不用考虑空回车的问题;
String name = scanner.nextLine();
那么问题来了上面这个是 String-->int ,那么 int-->String 该怎么写?
其实就是给int类型后面加一个空的字符串就可以了,因为int+String就变为拼接了
例: System.out.println(5 + "5"); // 55这个是字符串"55"
System.out.println(5 + 5 + "5" + 5 + 5);// 10555从左向右执行先5+5=10,然后10 + "5" = "1055"
七、常见错误
1、NumberFormatException :数字格式化异常,是类型转化的问题;如:如果将字符串转化为数字,系统就会出现这个错误
2、InputMisMatchException:输入类型不匹配;
3、ArrayIndexOutOfBoundsException:数组越界
4、NegativeArraySizeException:数组负长度异常
5、NullPointerException:空指针异常或对象为空了不能拿去用了
6、StackOverflowError:方法来回调用产生的问题
7、ArithmeticException:数学异常 整数/0了
八、面向对象类的设计(ArrayBox封装)
8.1 设计一个方法:用来添加元素
1、这里要是看不懂的话,在十六章会有个ArrayBox回顾,实际就是Java常说的ArrayList,下面写的是底层;
2、你会发现每个方法只完成一件事情
public class ArrayBox<E>{//这里的E表示泛型,就是什么类型都行
/*
* 设计一个方法:用来添加元素
* 假设:
* 用户:想要存椅子(元素),以前找到数组那个人,存储的时候你们自己找位置,数组满了
* 你们自己想办法
* 我是老师(ArrayBox类型一个具体对象):你们把椅子交给我(Box对象),我可以帮你们存,至
* 于我把椅子(元素)存在哪,长度空间(容量)够不够,不用你们管
* 考虑:
* 参数: 需要提供椅子的数量 int
* 返回值: 告知用户结果 是否成功 boolean
* 1. 你们把椅子(元素)交给我Box,我可以帮你们存
* 2. 我是Box,假设我把椅子(元素)存在一个教室(容器)里,教室(容器)里不止存一把椅子(元素),教室就是一个容器(相当于一个数组),
* 那么这个容器是属性(属性静态描述事情,我到底能存多少东西),不是方法,因为是我Box,容器是我的一部分
* 3. 那么我在存东西的时候,我要确定我的容器中的容量到底够不够存东西。
* 4. 我是老师(Box对象)就想让学生A(方法)去帮我,让A去确定这个教室(容器)有多少的容量空间,够不够存下。
* 考虑:
* 参数:老师需要告诉A有多少椅子(元素)(提供需要的最小容量值)
* 返回值:无
* 5. 发现存不下,进行扩容,这时A老大,就开始找小弟B,让他找新的教室,要比原来的旧教室大(扩容),最后算出该去找多大的教室
* 考虑:
* 参数 提供老师需要的最小容量
* 返回值 无
* 6. 老师知道了新教师的位置,还要找人C去搬椅子到新教师(负责创建一个新数组 将旧数组的元素全部移入新数组内),
* C同学做完之后会得到一个新教室
* 考虑:
* 参数:需要新数组长度 需要提供旧数组位置
* 返回值: 告诉新数组的位置 返回数组
* 老师是add,但是A找没找B干事情我不知道,B找没找C干事情老师和A也不知道
* 7. 通过ArrayBox就可以实现动态存储(扩容),那可以存,该怎么取,再取得过程中要考虑合理性(你不能存3个元素,却要去取6个数啊)
* 考虑:
* 参数: 提供获取元素的位置
* 返回值: 获取位置上的呢个元素
* 8. 让小D帮忙判断index范围是否合法
* 考虑:
* 参数: 提供index
* 9. 用来删除元素
* 考虑:
* 参数: 提供元素的位置
* 返回值: 删除掉的那个元素
* */
//描述事情
//属性
//2. 设置容器 Object表示什么都能存,不光光是数字
private Object[] elementDate; //数组长度一旦一定就不能改变
//记录容器中有效的元素个数
private int size = 0;
//定义常量
private static final int DEFAULT_CAPACITY = 10;
//让Test中可以获取size
public int size() {
return this.size;
}
//动态添加elementData长度不能每次都给他初时长度,这样特别不方便。为了节省空间内存
//用户知道做什么就让他做,用户不知道做什么,我们就帮他做
//构造方法,灵活运用内存空间
public ArrayBox() {//用户调用这个方法创建,说明用户不知道要做什么,这时我们就自动给他创建一个
elementDate = new Object[DEFAULT_CAPACITY];
}
public ArrayBox(int capacity){
elementDate = new Object[capacity];
}
//1.设定方法:用于存椅子(元素)
public boolean add(E element) {//E表示什么类型都可以,创建arrayBox对象时,规定的类型
//3. 确保我自己的属性数组的内部容量够不够用
this.ensureCapacityInternal(size + 1);//旧容器的椅子个数 +1
//如果上面的这一行代码可以执行完毕,说明elementData的教师肯定有空间
//想把element存入elementData里 , 有效元素个数多记录一个
elementDate[size++] = element;
//告知用户存储成功
return true;
}
//4. 设计方法:用来帮我做事(来确定容器中的容量有多少)
public void ensureCapacityInternal(int minCapacity) {
if (minCapacity - elementDate.length > 0) {//发现提供的元素比容器的容量大
//5. 证明存不下了 ,扩容 -》
this.grow(minCapacity);
}
}
//5. 小弟B扩容
public void grow(int minCapacity) {
//获取就容器的容量空间
int oldCapacity = elementDate.length;
//扩容需要合适的大小范围(左移一位乘2,右移一位除2)(这个算法比乘除运行块)
int newCapacity = oldCapacity + (oldCapacity >> 1); //旧空间 + 旧空间÷2,相当于旧空间的1.5倍
//如果按照这个1.5倍扩容后,发现所需要的空间还不够,这时就直接利用minCapacity
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity; //将老师提供的椅子数量直接变为新容器的容量
}
//经过上面一系列计算,最终获得一个最合理的长度 newCapacity
//按照新的长度,创建一个新的教室,然后还要将旧教室的椅子全部搬入到新教师中
elementDate = this.copyOf(elementDate, newCapacity);//elementDate旧教室 C同学做完之后会得到
// 一个新教室,你还要把这个新的教师赋给最开始老师找的教师,因为我是add,但是A找没找B干事情我不知道,B找没找
// C干事情我也不知道,所以老师还真为找的教师是elementDate
}
// 6. 负责创建一个新数组 将旧数组的元素全部移入新数组内
public Object[] copyOf(Object[] oldArray, int newCapacity) {
//创建一个新的数组
Object[] newArray = new Object[newCapacity];
//将旧数组元素全部移入新数组中
for (int i = 0; i < oldArray.length; i++) {
newArray[i] = oldArray[i];
}
return newArray;
}
//7.用来获取元素 也是泛型
public E getElement(int index) {
//需要检测index范围是否合法,因为你向我存了3个,但是你不能找我要6个啊!
this.rangeCheck(index); //找到D来帮忙检测
//如果上面一行代码可以走过去,证明index是合法的,就去真实的数据里面去取
return(E) elementDate[index];
}
//8. D帮忙判断index范围是否合法
private void rangeCheck(int index) {
//index=0表示第一个元素,size有效元素个数
if (index < 0 || index >= size) {
//通过输出 或 返回值 告知异常,所以我们可以参考数组的操作,自己定义一个异常(其实就是一个自己创建的类)来说明这个问题
throw new BoxIndexOutOfBoundsException("Index:" + index + ",Size" + size);
}
}
//9. 用来删除元素
public E remove(int index) {
//检测index范围是否合理
this.rangeCheck(index); //找到D来帮忙检测
//如果上面这行代码可以执行过去,没有异常 index合法
//先将index位置的旧值保存起来
E oldValue = (E)elementDate[index];
//10 20 30 40 50 60 0 0 0 ...--> 有效元素
//想删除30数字 arrayBox.remove(2);但是你要知道数组是无法删掉的
//10 20 40 40 50 60 0 0 0...其实就是将后面的数字覆盖给你想删除的数字的位置,然后后面以此类推向前赋值10 20 40 50 60 60 0 0 0...,
//但是还是剩下了最后的一个60,你想想用户实际他只能看到size,这时你就可以通过size控制
for (int i = index; i < size - 1 ; i++) {//size-1=4
//当前元素i,后面的元素是i+1
elementDate[i] = elementDate[i + 1];//从index开始 至 sizee-1位置 将后面位置元素一次向前移到覆盖
}
// elementDate[--size] = 0; //--size = 5
elementDate[--size] = null;//将末尾元素清空
//将旧值返回给用户
return oldValue;
}
}
public class Test {
public static void main(String[] args) {
//Test相当于用户
//想要存储元素
//1.创建对象
// ArrayBox arrayBox = new ArrayBox(); //存储以后长度还是改变,因为里面有方法实现
ArrayBox<Integer> arrayBox = new ArrayBox<Integer>(5); //存储以后长度还是改变,因为里面有方法实现,即便设置长度不够也没事,有扩容
//2.让box干活 存储一个元素,有了ArrayBox(),存多少个都行,动态存储
for (int i = 1; i <= 6; i++) {
arrayBox.add(i*10);//动态扩容
}//10 20 30 40 50 60 0 0 0
System.out.println("有效元素个数:" + arrayBox.size());//6
// System.out.println("真实数组长度:" + arrayBox.elementDate.length);
//3.有办法存,那怎么取?获取全部元素
System.out.print("所有元素:");
for (int i = 0; i < arrayBox.size(); i++) {
int value = arrayBox.getElement(i);
System.out.print(value + "\t");//10 20 30 40 50 60
}
System.out.println();
//4. 删除元素
int removeValue = arrayBox.remove(2);
System.out.println("想要删除的元素:" + removeValue);//30
System.out.print("删除后的元素:");
for (int i = 0; i < arrayBox.size(); i++) {
int value = arrayBox.getElement(i);
System.out.print(value + "\t");//10 20 40 50 60
}
//你会发现每个方法只做一件事情
// 然后你会发现ArrayBox对象中的所有方法都可以调用,但是Test类是用户访问的,
// 所以就会用到私有的修饰符private,不让用户看见,因为用户也看不懂
}
}
public class BoxIndexOutOfBoundsException extends RuntimeException{
//想要描述这个类是一个异常,让我们自己的异常是一个异常,通过
//基础extends 泛化(实现)implements
public BoxIndexOutOfBoundsException() {}
public BoxIndexOutOfBoundsException(String msg) {
super(msg);//msg提供父类
}
}