数组
10. 数组
1>.数组的说明
①. 是一种用于存储多个相同类型数据的存储模型
② .数组属引用类型,数组型数据是对象(object),数组中的每个元素相当于该对象的成员变量
③. 数组的长度在程序运行期间不可改变
2>.数组的使用
1. 数组的初始化和赋值
静态初始化:在创建的时候直接指定数组的元素,长度由数组中元素的个数决定
①.静态初始化,初始化数组与给数组元素赋值同时进行a .String names=new String[ ] {"唐智","杨幸","唐洋"} [ 标准 ]: b.String names= { "唐智","杨幸","唐洋" };[ 省略 ]
动态:在创建数组的时候只指定长度,系统会根据数组的数据类型分配默认值
②. 动态初始化,初始化数组与给数组元素赋值分开进行 int [ ]scores=new int[ 3 ];
2. 数组的长度
通过数组的length属性int namesLength=names.length;
注意:数组一旦初始化,程序运行期间,其长度就不可变
3. 如何遍历数组元素
①. for循环的方式
②. 增强for循环
4. 数组的初始化值 [ 掌握 ]
使用动态初始化数组的时候,其中的元素将会自动拥有一个默认值。规则如下:
①. 如果是整数类型,那么默认是0
②. 如果是浮点类型,那么默认是0.0
③. 如果是字符类型,那么默认是’\u0000’, 它是一个不可见的字符,不是空格
④. 如果是布尔类型,那么默认是false;
⑤. 如果是引用类型,那么默认是null
@Test public void fun1(){ //省略格式的静态初始化 //int []arrayA = int arrayA[]; int[]arrayA={10,20,30}; //静态初始化的标准格式,可以成分为两个步骤 int[]arrayB; arrayB=new int[]{10,20,30}; //动态初始化也可以拆分为两个步骤 int[] arrayC; arrayC=new int[5]; //静态初始化的省略格式,不能坼分为两个步骤 /*int [] arrayD; arrayD={10,20,30};*/ //直接打印数组的名称,得到的是数组对应的,内存地址哈希值 System.out.println(arrayD);
@Test public void fun1() { //1.如果定义一个数组 //1.1数组的声明 String[]names; int[]scores; //1.2初始化 //第一种:静态初始化,初始化数组与给数组元素赋值同时进行 names=new String[]{"唐智","杨幸","唐洋"}; //第二种:动态初始化,初始化数组与给数组元素赋值分开进行 scores=new int[3]; //2.如何调用相应的数组元素,通过数组元素的下角标的方式来调用 //下表从0开始,到n-1结束。其中n表示的数组的长度 scores[0]=60; scores[1]=70; scores[2]=80; //3.数组的长度:通过数组的length属性 int namesLength=names.length; System.out.println("数组的长度是:"+namesLength); //4.如何遍历数组元素 for(int i=0;i<namesLength;i++){ System.out.println(names[i]); } for(String name:names){ System.out.println(name); } } }
3>.Java中的内存划分
①.栈(Stack):存放的都是方法中的局部变量。方法的运行一定要在栈当中运行,使用完毕以后会立即消失
局部变量:方法的参数,或者是方法{ }内部的变量
作用域:一旦超出作用域,立刻从栈内存当中消失
②. 堆(Heap):凡是new出来的东西,成员变量,都在堆当中,使用完毕,会被垃圾回收器空闲时回收
堆内存里面都有一个地址值:16进制
堆内存里面的数据,都有一个默认值。规则:
如果是整数 默认是0 如果是浮点数 默认是0.0 如果是字符 默认为' \u0000', //在unicode中16进制的都是字符,是一个不可见字符,但是并不是空格 如果是布尔 默认是false 如果是引用类型 默认是null
③.方法区(Method Area):储存.class相关信息,包含方法的信息
④.本地方法栈(Native Method Stack):与操作系统相关
⑤. 寄存器(pc Register):与CPU相关,保证方法的调用
4>. 一维数组的内存图
5>. 数组常见的异常
①. 数组索引越界异常:ArrayIndexOutOfBoundsException
②. 空指针异常 [ NullPointerException ]:所有的引用类型变量,都可以赋值一个null值。但是代表其中什么都没有。 数组必须进行new初始化才能使用其中的元素,如果只有赋值一个null,没有进行new创建 那么将会发生:空指针异常 NullPointerException
访问的数组已经不再指向堆内存的数据,造成空指针异常
③. null:空值,引用数据类型的默认值,表示不指向任何有效对象
//索引越界:访问了数组中不存在的索引对应的元素,造成索引越界问题 int [ ] arr=new int[ 2 ]; System.out.print(arr[3]); // 原因:忘了new int []arr=null; System.out.println(arr[0]);
6>. 数组的反转
重点是一个元素中各个元素的值不同了,发生了反转 数组元素的反转 本来的样子:[1,2,3,4] 之后的样子:[4,3,2,1] 1.数组元素的反转,其实就是对称位置的元素交换 2.通常遍历数组用的是一个索引 int i=0; 现在表示对称位置需要两个索引 int min=0; int max=array.length-1; 3.如何交换两个变量的值? 用第三个变量倒手 4.什么时候停止交换? ①.min=max ②.min<max 什么时候应该交换? min<max
int [] arr={10,6,5,55,99,66}; //遍历打印数组本来的样子 for (int i = 0; i < arr.length; i++) { System.out.print(arr[i]+"\t"); } System.out.println(""); System.out.println("=========="); for(int min=0,max=arr.length-1;min<max;min++,max--){ int temp=arr[min]; arr[min]=arr[max]; arr[max]=temp; } //再次打印输出数组后来的样子 for (int i = 0; i < arr.length; i7.7.++) { System.out.p.rint(arr[i]+"\t"); }
7>. 数组内容相同 [ 标记的方法很重要 ]
public class Demo4 { public static void main(String[] args) { //定义两个数组,分别使用静态初始化完成数组元素的初始化 int [] arr1 ={11,22,33}; int [] arr2 ={11,22,33}; boolean compare1 = compare(arr1, arr2); System.out.println("两个数组是否相同?"+compare1); } public static boolean compare(int[]arr1,int[]arr2){ //首先比较数组长度,如果长度不相同,数组内容肯定不相同,返回false if(arr1.length!=arr2.length){ return false; } //其次遍历,比较两个数组中的每一个元素,只要有元素不相同,返回false for(int i=0;i<arr1.length;i++){ if(arr1[i]!=arr2[i]){ return false; } } //最后 return true; } }
标记的思想 [ 重要 ] public class Test01 { public static void main(String[] args) { int[]arr={11,22,33,44}; int[]arr2={11,22,33,44}; //调用方法,用变量接收 boolean flag=compare(arr,arr2); System.out.println("flag = " + flag); } public static boolean compare(int[] arr, int[] arr2) { //首先比较长度,如果长度不相同,数组的内容肯定不痛 if(arr.length!=arr2.length){ return false; } //标记 boolean flag=true; //遍历数组 for (int i = 0; i < arr.length; i++) { if(arr[i]!=arr2[i]){ //重新对标记赋值 flag=false; } } //当循环结束之后,不管flag里的值是什么,直接返回即可 return flag; } }
8>. 数组作为方法参数_传递地址 [ 掌握 ]
①. 数组可以作为方法的参数,当调用方法的时候,向方法的小括号进行传参,传递进去的其实是数组的地址值
②. 数组作为方法的返回值,返回的其实也就是数组的地址值
public class Demo4 { public static void main(String[] args) { int a=10; int b=5; int c=6; int []arr=calculate(a,b,c); System.out.println("数组的返回值其实是数组的地址值"+arr); System.out.println("三个数只和是:"+arr[0]); System.out.println("三个数的平均数是:"+arr[1]); } public static int[] calculate(int a,int b,int c){ int sum=a+b+c; int avg=sum/3; int []arr={sum,avg}; return arr; } }
9>. 练习 [ 重点掌握其中的思路 ]
/* 1.键盘录入6个int类型的数据存数数组arr中 2.将arr数组中的内容反转 3.将翻转后的数组角标为奇数的互相交换 1和3换, 3和5换,以此类推 4.最后将数组最后一个角标为奇数的元素 和数组中第一个角标为奇数的元素交换 5.打印最终的数组(实现了1-4步之后的数组) 6.如:用户输入的6个int数字为[1,2,3,4,5,6],最后输出的结果为[6, 5, 4, 1, 2, 3] * */ public class HomeWord3 { public static void main(String[] args) { Scanner sc=new Scanner(System.in); int[]arr=new int[6]; //1.键盘录入6个int类型的数据存数数组arr中 for (int i = 0; i < arr.length; i++) { System.out.println("请键盘输入第"+(i+1)+"个数"); arr[i]=sc.nextInt(); } System.out.println("原来的数组是:"); print(arr); System.out.println(""); //2.将arr数组中的内容反转 System.out.println("反转后的数组是"); arrReverse(arr); print(arr); System.out.println(""); //3.将翻转后的数组角标为奇数的互相交换 1和3换, 3和5换,以此类推 System.out.println("将翻转后的数组角标为奇数的互相交换"); arrChange(arr); print(arr); System.out.println(); //4.最后将数组最后一个角标为奇数的元素 和数组中第一个角标为奇数的元素交换 System.out.println("最终的数组是:"); arrchangeFirstLast(arr); } //4.最后将数组最后一个角标为奇数的元素 和数组中第一个角标为奇数的元素交换 public static void arrchangeFirstLast(int[]arr){ //如果数组长度是偶数 if(arr.length%2==0){ int temp=arr[1]; arr[1]=arr[arr.length-1]; arr[arr.length-1]=arr[1]; }else { int temp=arr[1]; arr[1]=arr[arr.length-2]; arr[arr.length-2]=arr[1]; } } //3.将翻转后的数组角标为奇数的互相交换 1和3换, 3和5换,以此类推 public static void arrChange(int[] arr) { for (int i = 1; i < arr.length-2; i++) { // 判断一下索引是否是奇数 if(i%2!=0){ int temp=arr[i]; arr[i]=arr[i+2]; arr[i+2]=temp; } } } //2.将arr数组中的内容反转 public static void arrReverse(int[] arr) { for (int i = 0,j=arr.length-1; i < j; i++,j--) { int temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } } public static void print(int[]arr){ System.out.print("["); for (int i = 0; i < arr.length; i++) { if(i==arr.length-1){ System.out.print(arr[i]+"]"); }else{ System.out.print(arr[i]+","); } } } }
/
//要求掌握第二个和第四个 public class HomeWork4 { /* 1.键盘录入10个整数存入数组中 2.定义一个方法将奇数放在数组的左侧,偶数放在数组的右侧 3.定义一个方法打印原数组和处理后的数组 4.定义一个方法传入一个int类型数组,输出这个数组中只出现一次的数字及个数 */ public static void main(String[] args) { Scanner sc=new Scanner(System.in); int[]arr=new int[6]; //1.键盘录入6个int类型的数据存数数组arr中 for (int i = 0; i < arr.length; i++) { System.out.println("请键盘输入第"+(i+1)+"个数"); arr[i]=sc.nextInt(); } System.out.println("原来的数组是:"); print(arr); System.out.println(""); //2.定义一个方法将奇数放在数组的左侧,偶数放在数组的右侧 System.out.println("左侧奇数,偶数右侧"); swap(arr); print(arr); System.out.println(""); //3.定义一个方法传入一个int类型数组,输出这个数组中只出现一次的数字及个数 findFirstCount(arr); } //3.定义一个方法传入一个int类型数组,输出这个数组中只出现一次的数字及个数 public static void findFirstCount(int[]arr){ int count=0; System.out.print("元素中只出现一次的元素是:"); for (int i = 0; i < arr.length; i++) { int arrCount=0; for (int j = 0; j < arr.length; j++) { if(arr[i]==arr[j]){ arrCount++; } } if(arrCount==1){ System.out.print(arr[i]+" "); count++; } } System.out.println(""); System.out.println("元素中只出现一次的总个数是"+count); } /* 思路: 定义一个变量,这个变量将来作为奇数元素存放的索引位置 遍历数组 判断数组中的元素是否是奇数 如果是奇数,那么这个奇数就存放在数组的arr[index]位置上 切记每次交换之后index一定要++ * */ //2.定义一个方法将奇数放在数组的左侧,偶数放在数组的右侧 public static void swap(int[] arr) { int index=0; for (int i = 0; i < arr.length; i++) { //如果是奇数 if(arr[i]%2!=0){ if(i==0){ //只要进行了交换,index一定要进行++ index++; }else{ //交换位置 int temp=arr[index]; arr[index]=arr[i]; arr[i]=temp; //只要进行了交换,index一定要进行++ index++; } } } } //打印输出 public static void print(int[]arr){ System.out.print("["); for (int i = 0; i < arr.length; i++) { if(i==arr.length-1){ System.out.print(arr[i]+"]"); }else{ System.out.print(arr[i]+","); } } } }
10>. 关于数组的常见几种算法
1. 冒泡排序
原理:两个相邻位置比较,如果前面的元素比后面的元素大就换位置
public class DemoArr { public static void main(String[] args) { int []arr={1,5,88,99,11}; bubbleSort(arr); print(arr); } /* 冒泡排序 : 1. 返回值类型,void 2. 参数列表 ,int[]arr 第一次: arr[0]与arr[1], arr[1]与arr[2], arr[2]与 arr[3], arr[3]与 arr[4] 比较 4次 第二次: arr[0]与arr[1], arr[1]与arr[2], arr[2]与 arr[3] 比较 3 次 第三次: arr[0]与arr[1], arr[1]与arr[2] 比较 2次 第四次: arr[0]与arr[1] 比较 1 次 * */ public static void bubbleSort(int[]arr){ //如果有n个数据进行排序,总共需要比较n-1次 //外循环只需要比较arr.length-1次就可以了 for (int i = 0; i < arr.length-1; i++) { //内循环 //-1为了防止索引越界 -i 是为了提高效率,每次完后就少判断一次 for (int j = 0; j < arr.length-1-i; j++) { if(arr[j]>arr[j+1]){ int temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } } } } //打印数组 public static void print(int[]arr){ System.out.print("["); for (int i = 0; i < arr.length; i++) { if(i==arr.length-1){ System.out.print(arr[i]+"]"); }else{ System.out.print(arr[i]+", "); } } } }
2. 二分查找 [ 折半查找 ]
二分查找:查找元素对应的索引
前提:数组元素有序
public class Demo1{ public static void main(String[] args) { int[]arr={11,22,33,44,55,66,77}; int index=getIndex(arr,45); System.out.println("index = " + index); } //二分查找: 返回值类型int,参数列表arr[] ,value public static int getIndex(int[]arr,int value){ int min=0; int max=arr.length-1; int mid=(min+max)/2; //当中间值不等于要找的值,就开始循环查找 while(arr[mid]!=value){ //当中间值小于了要找的值,让最小的索引改变 if(arr[mid]<value){ min=mid+1; //当中间值大于了要找的值,让最大的索引改变 }else if(arr[mid]>value){ max=mid-1; } //无论最大还是最小改变,中间索引都会随之改变 mid=(min+max)/2; //如果最小索引大于了最大索引,就没有查找到返回-1 if(min>max){ return -1; } } return mid; } }
11 . 二维数组的使用[ 主要看代码 ]
1. 二维数组的初始化
①. 静态初始化:String[][]names=new int[][]{{1,2,3},{3,4,5},{6}};
②. 动态初始化:a. names=new String[6][5]; b.ames=new String[6][]
2. 如何来引用具体的某一个元素
int[ ][ ]i=new int[3][2]; i[1][0]=90; i[2][1]=100;
3.数组的长度
整个二维数组的长度:
int length=i.length; System.out.println("整个二维数组的长度:"+length);
二维数组中元素的长度
System.out.println("二维数组的第一行有多少元素"+i[0].length);
4. 遍历二维数组
//4.如何遍历二维数组scores=new int[][]{{1,2,3},{3,4,5},{6}};//静态初始化 for(int j=0;j<scores.length;j++){//控制行数 for(int k=0;k<scores[j].length;k++){ System.out.print(scores[j][k]+" "); } System.out.println(); }
5. 二维数组内存结构
注意:特殊写法情况:int[ ] x,y[ ]; x是一维数组,y是二维数组。
一维数组:int[] x 或者int x[]
二维数组:int[][] y 或者 int[] y[] 或者 int y[][]
int[]x,y[]; //int [] x;一维 //int [] y[];二维
@Test public void fun1(){ int[][]scores; String[][]names; //1.二维数组的初始化 scores=new int[][]{{1,2,3},{3,4,5},{6}};//静态初始化 names=new String[6][5];//2.动态初始化的方式一:6个小组5个人 names=new String[6][];//动态初始化的方式二:有6个小组,每个小组人数不定 names[0]=new String[5]; names[0]=new String[6]; names[0]=new String[7]; names[0]=new String[8]; //错误的初始化方式 // names=new String[][]; // names=new String[][5]; //2.如何来引用具体的某一个元素 int[][]i=new int[3][2]; i[1][0]=90; i[2][1]=100; //3.数组的长度 //整个二维数组的长度 int length=i.length; System.out.println("整个二维数组的长度:"+length); //二维数组中元素的长度 System.out.println("二维数组的第一行有多少元素"+i[0].length); //4.如何遍历二维数组scores=new int[][]{{1,2,3},{3,4,5},{6}};//静态初始化 for(int j=0;j<scores.length;j++){//控制行数 for(int k=0;k<scores[j].length;k++){ System.out.print(scores[j][k]+" "); } System.out.println(); } }
6. 二维数组的练习
@Test public void fun2(){ int[][]arr=new int[][]{{3,8,2},{2,7},{9,0,1,6}}; int sum=0; for(int i=0;i<arr.length;i++){ for(int j=0;j<arr[i].length;j++){ sum=sum+arr[i][j]; } } System.out.println("数组的和是 :"+sum); }
9. Arrays
java.util.Arrays:是一个与数组相关的工具类,里面提供了大量的静态方法,用来实现数组常见的操作
public static String toString(数组):将参数数组变成字符串(按默认格式:[ 元素1 , 元素2 ,元素3 ])
public static void sort( 数组):按照默认升序(从小到大)对数组的元素进行排序
备注:①.如果是数值,sort默认按照升序从小到大
②. 如果是字符串,sort默认按照字母升序
③. 如果是自定义类型,那么这个自定义的类需要有Comparable或 Comparator 接口的支持
int []intArray={10,20,30}; String instr= Arrays.toString(intArray); System.out.println(instr);//[10, 20, 30] int [] array1={1,2,66,99,77,55,21}; Arrays.sort(array1); System.out.println(Arrays.toString(array1));//[1, 2, 21, 55, 66, 77, 99]
//需求:将一个随机字符串中得到所有字符升序排序,并倒序打印 //自定义随机的字符串 String line="adxcvewgjgn"; //转换成字符数组 char[]ch=line.toCharArray(); //升序排序 Arrays.sort(ch); //倒序打印 System.out.print("升序后倒序打印如下:"); for(int i=ch.length-1;i>=0;i--){ System.out.print(ch[i]+"\t"); }
11. 方法
1.方法的概述
方法是将具有 [ 独立功能的代码块 ] 组织成为一个整体,使其具有特殊功能的代码集
注意:①. 方法必须先创建才能使用,该过程称为方法定义
②. 方法创建后并不是直接运行的,需要手动使用后才能执行,该过程称为方法调用
2. 方法的调用
①.单独调用: 方法名称(参数)
②.打印调用: System.out.println(方法名称(参数));
③.赋值调用: 数据类型 变量名称=方法名称(参数)
public class MethodDefine { public static void main(String[] args) { sum(10, 20);//①.单独调用: 方法名称(参数) System.out.println(sum(10, 20));//②.打印调用: System.out.println(方法名称(参数)); int num1=sum(10, 20);//③.赋值调用: 数据类型 变量名称=方法名称(参数) } public static int sum(int a,int b){ System.out.println("11"); int result=a+b; return result; } }
3.对比有参数和无参数
方法的参数就是定义一些变量,当方法被调用的时候,用来接收数据使用的 [ 掌握 ]
有参数:小括号当中有内容,当一个方法需要一些数据条件,才能完成任务的时候,就是有参数
例如两个数字相加,必须知道两个数字各是多少,才能相加
无参数:小括号当中留空。一个方法不需要任何数据条件,自己就能独立完成任务,就是无参数
例如定义一个方法,打印固定10次Helloword
4.对比有返回值和没有返回值
有返回值: 定义一个方法,用来 [求出] 两个数字之和(你帮我算,算完之后把结果告诉我)
无返回值: 定义一个方法,用来 [打印] 两个数字之和(你来计算,算完之后你自己负责显示结果,不用告诉我)
注意事项:
①. 对于有返回值的方法,可以使用单独调用、打印调用、或者赋值调用
②. 对于没有返回值的方法,只能使用单独调用,不能使用 打印调用、或者赋值调用
public class MethodDefine { public static void main(String[] args) { } //我是一个方法,我负责两个数字相加 //我有返回值int,谁负责调用我,我就把结果告诉谁 public static int getSum(int a, int b){ int result=a+b; return result; } //我是一个方法,我负责两个数字相加。 //我没有返回值,不会把结果告诉任何人,而是我自己进行打印输出 public static void getSum2(int a, int b){ int result=a+b; System.out.println("结果是:"+result); } }
5.类的方法:提供某种功能的实现
①. 实例:
格式:权限修饰符 返回值类型(void:无返回值/具体的返回值) 方法名(形参){ } public void eat(){//方法体} public String getName(){ } public void setName(String n){ }
②. 关于返回值类型:void:表明此方法不需要返回值,有返回值的方法:在方法的最后一定有return + 返回值类型对应的变量[ 记忆:void 与return不可以同时出现一个方法内。像一对“冤家”。]
③. 方法内可以调用本类的其他方法或属性,但是不能在方法内再定义方法!
public void info(){ eat();//可以调用本类的其他方法 sleep();//可以调用本类的其他方法 public void breath(){//错误的写法 } }
④.如下写法是正确的 [ 掌握 ]
public static void method(){ return; }
6. 方法中传参的特点 [ 重点 ]
①. 如果方法的参数是基本数据类型,那么形参的改变,不会影响实参 [ 传递的是具体的数据 ]
②. 对于引用类型的参数,形参参数的改变,影响实际参数的值 [ 传递的是地址值 ]
12. 方法的重载 [ 重载的要求 ]
①. 在同一个类中
②. 方法名必须相同
③. 方法的参数列表不同 [ 参数的个数不同、参数的类型不同、参数的顺序 ]
补充:①. 方法的重载与方法的返回值类型没有关系 ②. 方法的重载与参数的名称名称无关
13.面向对象编程
面向对象编程的三条主线:
①. 类及类的构成成分:属性、方法、构造器、代码块、内部类
②. 面向对象编程的特征:封装性、继承性、多肽性(抽象类)
③. 其他的关键字:this、super、package、import、static、final、abstract、interface…
面向对象:当需要实现一个功能的时候,不关心具体的步骤,而是找一个已经具有该功能的人,来帮我做事儿
1. java中的类的概念与设计
类和对象是面向对象的核心概念
类 是对一类事物描述,是抽象的、概念上的定义
对象 是实际存在的该类事务的每个个体,因此也称实例
2.面向对象思想的落地法则一:
①. 设计类,并设计类的成员(成员变量&成员方法)
②. 通过类,来创建类的对象(也称作类的实例化)
③. 通过“对象.属性” 或“对象.方法”来调用,完成相应的功能
//1.属性 String name; int age; String sex; //2.方法 public void eat(){ System.out.println("人吃东西"); }
3. 创建的多个对象,彼此各自拥有一套类的属性,当对其中一个对象的属性的属性进行修改时,不会影响到其他对象的属性值
4.类的属性(成员变量 v 局部变量)
1. 变量和局部变量的相同点
①. 遵循变量声明的格式 ②. 都有作用域
2. 不同点
①. 定义的位置不一样
局部变量:在方法的内部,参数的形参部分
成员变量: 在方法的外部,直接写在类中
②. 作用范围不一样
局部变量:只有方法当中才可以使用,出了方法就不能再用
成员变量:整个类全部可以通用
③. 默认值不一样
局部变量:没有默认值,如果要使用,必须手动进行赋值
成员变量:如果没有赋值,会有默认值,规则和数组一样
④. 内存的位置不一样
局部变量:位于栈内存中
成员变量: 位于堆内存中
⑤. 生命周期不一样
局部变量:随着方法进栈而诞生,随着方法出栈而消失
成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失
byte short int long ==>0 float double ==>0.0 char ==>空格 boolean ==>false 引用类型变量==>null
3. 变量的分类
①. 按照数据类型的不同:基本数据类型(8种)&引用数据类型
②. 按照声明的位置的不同:成员变量&局部变量
5. 一个对象的内存图 [ 掌握 ]
两个对象使用同一个方法的内存图 [ 原理都类似 ]
两个引用指向同一个对象的内存图 [ 原理都类似 ]
6. 使用对象类型作为方法的参数
当一个对象作为一个参数,传递到方法中时,实际上传递进去的是对象的地址值
7. 面向对象三大特征之封装性
1.封装的思想
问题: 当创建了类的对象以后,如果直接通过了"对象.属性"的方式对相对的对象赋值的话,可能会出现不满足实际情况的意外,我们考虑不让对象来直接作用属性,而是通过"对象.方法"的形式,来控制对象对属性的访问。实际情况中,对属性的要求就可以通过方法来实现。
解决的方法(封装性的思想):
①. 将类的属性私有化
②. 提供公共的方法(setter &getter)来实现
private 修饰的属性只能在本类中被调用,出了此类就不能被调用
//private 修饰的属性只能在本类中被调用,出了此类就不能被调用 private String name; private int legs; public int getLegs() { return legs; } public void setLegs(int legs) { this.legs = legs; } public String getName() { return name; } public void setName(String name) { this.name = name; }
//练习封装 public class TestPerson { public static void main(String[] args) { person tp=new person(); tp.setAge(30); System.out.println(tp.getAge()); } } class person{ private int age; public void setAge(int ages){ if(ages>0&&ages<130){ age=ages; }else{ System.out.println("你输入有误!"); } } public int getAge(){ return age; } }
2.Java权限修饰符[ public、protected、缺省、private ]
8. 类的成员之三:构造器(构造方法)
1. 构造器的作用 [ 掌握 ]
①. 创建对象 Person p=new Person(); ②. 给创建对象的属性赋值 [ 列如那些默认值 ]
2. 构造器的创建
格 式:权限修饰符 类名(形参){}
①. 构造方法不能return一个具体的返回值
②. 构造方法不要写返回值类型,连void都不写
3. 构造器的说明
①. 设置类时,若不显示声明类的构造器的话,程序会默认提供一个空参的构造器
②. 一旦显示的定义类的构造器,那么默认的构造器就不在提供
③. 类的多个构造器之间构成重载
public class TestPerson { public static void main(String[] args) { Person p1=new Person(); Person p2=new Person("小智"); System.out.println(p2.getName()); } } class Person{ //属性 private String name; private int age; //构造器 public Person(String n){ name=n; } public Person(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
9. this关键字
1. this关键字作用修饰属性,方法,构造器
①. 当局部变量 [ 形参 ]与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表名该变量时类成员
②. 在任意方法内,如果使用当前类的成员变量或成员方法可以在前面添加this,增强程序的阅读性
③. this可以作为一个类中,构造器相互调用的特殊格式
2. this关键字的注意事项
①. 在构造器内部必须声明在首行
②. 若在一个雷中有n个构造器,那么最多有n-1个构造器中使用了this();
this表示哪个对象调用,this就代表哪个对象
10. 定义一个标准的类
①. 所有的成员变量都要使用private关键字修饰
②. 为每一个成员变量编写一对儿Getter/setter方法
③. 编写一个无参数的构造方法
④. 编写一个全参的构造方法
//这样标准的类叫做JavaBean public class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public Student() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
11.匿名类对象
1. 匿名类对象的使用
当我们只需要一次调用类的对象时,我们就可以考虑使用匿名方式创建类的对象
特点:创建的匿名类的对象只能够调用一次
匿名对象只能使用唯一的一次,下载再用不得不再创建一个新对象
2. 匿名类对象作为方法的参数和返回值
public class StudentT { public static void main(String[] args) { //普通使用方式 /* Scanner sc=new Scanner(System.in); int num=sc.nextInt();*/ //使用匿名对象进行传参 methodParam(new Scanner(System.in)); System.out.println("========="); Scanner sc=methodReturn(); int num2=sc.nextInt(); System.out.println("输入的是:++"+num2); } //使用匿名对象作为参数 public static void methodParam(Scanner sc){ int num=sc.nextInt(); System.out.println("输入的是:"+num); } //使用匿名对象作为返回值 public static Scanner methodReturn(){ return new Scanner(System.in); } }
12. 继承
继承的好处:提高代码的复用性,提高代码的维护性。缺点:耦合性太强
1>. 继承的概述
①. 继承是面向对象三大特征之一。可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法
继承性在java中的体现有 extends 或 implements
②. 格式:public class 子类名 [ 派生类 ] extends 父类名[ 基类、超类 ]{ }
③. 继承的作用:共性的抽取
④. 继承中子类的特点:①.子类可以有父类的内容 ②.子类还可以有自己特有的内容
2>. 继承的注意事项
①.Java中只支持单继承,不支持多继承 [ 类和类之间是单继承 ]
②. Java中类支持多级继承
③.一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类
3>. 什么时候使用继承
继承体现的关系:is a
假设法:我们有两个类A和B,如果他们满足A是B的一种,或者B是A的一种,就说明他们存在继承关系,这个时候就考虑使用继承来体现,否则就不能滥用继承
举例:苹果和水果、猫和动物、猫和狗
4>. 继承访问的特点 [ 掌握 ]
1. 继承中在子类方法中访问一个变量特点
①. 子类局部范围找
②. 子类成员范围找
③. 父类成员范围找
④. 如果都没有就报错(不考虑父亲的父亲…)
2. 继承中成员方法的访问特点 [ 通过子类对象访问一个方法 ]
①. 子类成员范围找
②. 父类成员范围找
③. 如果都没有就报错(不考虑父亲的父亲)
3. 继承中构造方法的访问特点
①. 子类中所有的构造方法默认都会访问父类中的无惨构造方法
②. 每一个子类构造方法的第一条语句默认都是:super()
③. 如果父类中没有无参构造方法,只有带参构造方法,该怎么办?
a. 通过使用super关键字去显示的调用父类的带参构造方法
b. 在父类中自己提供一个无参构造方法 [ 推荐 ]
疑问:在普通的成员方法中是否可以调用父类的构造方法? [ 掌握 ]
不管是无参还是有参,都不可以,构造方法的调用只能在子类创建对象的时候才能初始化父类, 父类的构造方法一定是在子类创建对象的时候就要执行,初始化的动作
5>. super关键字 [ 修饰属性、方法、构造器 ]
this: 代表本类对象的引用
super:代表父类储存空间的标识(可以理解为父类对象的引用)
super 可以用来修饰属性、方法、构造器*
①. 当子类与父类有同名的属性时,可以通过super.此属性显示的调用父类中声明的属性,若想调用子类的同名的属性this.此属性
②. 当子类重写父类的方法以后,在子类中若想再显示父类的被重写的方法,就需要使用"super.方法"
③. super修饰构造器。通过在子类中使用"super()形参列表"来显示的调用父类中的构造器
注意:
a. 在构造器内部,super(形参列表)必须要声明在首行。
b. 在构造器内部,this(形参列表)或super(形参列表)只能出现一个
c. 当构造器中,不显示的调用 "this(形参列表)"或super(形参列表)其中任何一个,默认调用是父类空惨构造器
6>. this和super的内存分配图
7>. 重写 [ 覆盖 ]
1. 前提条件
有子类继承父类
2. 为什么要重写的方法
子类继承父类以后,若父类的方法对子类不适用,那么子类可以对父类的方法重写、覆盖、覆写
3. 重写有一定的规则(两同两小一大原则)
①. 要求子类方法的方法名、参数列表必须和父类的一样
②. 子类方法的修饰符>=父类方法的修饰符
③. 若父类方法抛异常,那么子类方法抛的异常类型<父类
④. 子父类的方法必须同为static或同为非static的
⑤. 父类的返回值类型小于子类的返回值类型(同一类型下比较)
- java疯狂讲义中说重写返回值类型要比父类小,在eclipse中验证,重写返回值类型必须一致,那么Java重写返回值类型必须一样吗?
java 5或者以前,必须一样,java 7 java 8可以不同,但是必须是父类返回值的派生类。
4. 重写要注意的事项
私有方法不能被重写(父类私有成员子类是不能被继承的)
8. 继承的练习
13. package 和 import
1>. package
- 声明源文件所在的包,写在程序的第一行,每‘.’一次,表示一层文件目录,包名都要小写
格式:package 包名(多级包用.分开);
规范:package com.itheima;
2>.import
①. 显式导入指定包下的类和接口
②. 写在包的声明和源文件之间
③. 如果需要引用多个类或接口,那么就并列写出
④. 如果导入的类时java.lang 包下的,如:System.out.println(),就不需要显式的声明
14. 修饰符
1>. 权限修饰符
2>. 状态修饰符
1.final [ 用来修饰类、属性、方法 ]
在 Java中声明类、属性、方法时,可使用关键字final来修饰,表明"最终"
①. final标记的类不能被继承(如:String、StringBuffer 、System)
②. final标记的方法不能被重写 (如:object类中的getClass)
③. final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次
a. final标记的成员变量必须在声明的同时或每个构造器方法中或代码块中显示赋值,然后才能使用
b. final double PI=3.14;
注意:变量用static和final修饰,称为全部常量
2. final修饰局部变量 [ 了解 ]
变量是基本数据类型:final修饰指的是基本类型的 数据值 不能发生改变
变量是引用类型:final修饰指的是引用类型的 地址值 不能发生改变,但是地址里面的内容是可以发生改变的
public class Student { public int age=20; } public class FinalDemo { public static void main(String[] args) { //修饰基本类型变量,值不能变 final int age=20; //age=100;报错 System.out.println(age);//20 //final修饰引用类型变量:地址值不能变,但是变量可以变 final Student student=new Student(); student.age=100; System.out.println(student.age);//100 //student=new Student(); 报错 } }
3. static
1. static的概述
一旦使用了static关键字,那么这样的内容不再属于对象自己,而是属于类的,所有凡是本类的对象都共享同一份
static,静态的,可以用来修饰属性,方法,代码块(或初始化 ),内部类
2. static修饰属性(类变量):
①. 由类创建的所有对象,都共用这一属性 [ 这也是我们判断是否使用静态关键字的条件 ]
②. 当其中一个对象对此属性进行修改,会导致其他对象对属性的一个调用。vs 实例对象(非static修饰的属性,各自对象拥有一套剧本)
③. 类变量时随着类的加载而加载的,而且独一份
④. 静态的变量可以直接通过"类.类变量"的形式来调用
⑤. 类变量的加载要早于对象(全局变量(实例变量)随着对象的创建而被加载的)。所以当有了对象以后,可以"对象.类变量"
⑥. 类变量存在于静态域中 [ 方法区中 ]
类变量的标准用法:public static final String coutry="中国";
2.static修饰方法(类方法)
①随着类的加载而加载,在内存中也是独一份
②可以直接通过"类.类方法"的方式调用
③内部可以调用静态的属性或静态的方法,而不能调用非静态的属性或方法。非静态的方法时可以调用静态的属性或方法的
注意:
①静态结构(static的属性、方法、代码块、内部类)的生命周期是要早于非静态的结构,同时被回收也要晚于非静态的结果
②静态的方法时不能有this、super关键字
15. 类的成员四 → 始化快(代码块)
代码块如果有修饰的话,那么只能使用static
1. 非静态代码块
①. 可以对类的属性进行初始化操作,可以也可以调用本类声明的方法(静态&非静态的)
②. 里面可以有输出语句
③. 一个类中可以有多个非静态的代码块,多个代码块按照顺序执行
④. 每创建一个类的对象,非静态代码块就加载一次
⑤. 非静态代码块的执行要早于执行构造器
2. 静态代码块
①. 里面可以有输出语句
②. 随着类的加载而加载,而且只能加载一次
③. 多个静态代码块之间,按照顺序 构执行
④. 静态的代码块中只能执行静态的结构(类属性、类方法)
⑤. 静态内容总是优先于非静态,所以静态代码块比构造器方法先执行
3. static的典型用途
用来一次性对静态成员变量进行赋值
public class Demo6 { static String name; static{ name="小智"; System.out.println("22"); } public static void main(String[] args) { Demo6 demo6=new Demo6(); System.out.println("name"); } } 父类静态代变量、 父类静态代码块、 子类静态变量、 子类静态代码块、 父类实例成员变量、 父类非静态代码块 父类构造函数、 子类实例成员变量 子类非静态代码块 子类构造函数。
//
class test{ public test(){ System.out.println("我是测试方法"); } } class test2{ public test2(){ System.out.println("我是测试01方法"); } } class test3{ public test3(){ System.out.println("我是测试02方法"); } } class A{ private static test t = new test(); private test2 t2 = new test2(); { System.out.println("A类的代码"); } static { System.out.println("A类静态"); } public A(){ System.out.println("A类的构造方法"); } public A(String s){ System.out.println("A类传值的构造方法"); } } class B extends A{ private static test t = new test(); private test3 t2 = new test3(); { System.out.println("B类的代码"); } static { System.out.println("B类静态"); } public B(){ System.out.println("B类的构造方法"); } public B(String s){ //super(); System.out.println("B类传值的构造方法"); } } public class DemoTest { public static void main(String[] args) { new B("xiaozhi"); } }