疯狂Java学习笔记(008)
回顾
一、数组
1.数组的概念:
同种数据类型若干个数值的组合.称为数组.
实际上就是多个内存空间(变量)的组合.
数组的元素值可以在数据类型的表数范围内发生变化!!!
2.数组的元素:
element:数组中的数值就称为数组的元素!
3.数组的长度:
length:就是数组中元素的个数.
4.元素的索引:
index:数组中元素的编号.从0开始.最大值是数组的长度-1.
5.数组的初始化方式:
初始化指的是数组中的元素第一次赋值,以后再赋值就不是初始化.
1.> 动态初始化:只指定数组的长度.由系统赋默认初始值(0/0.0/false/null).
int[] arr = new int[5];
在堆空间中开辟5个能存放int型数据的空间,赋予默认的初始值,并把地址值返回给变量arr.
2.静态初始化:在定义数组时指定数组的初始化值.此时不能指定数组的长度!(静态和动态不能同时使用!!)
int[] arr = new int[]{1,2,3};
//在堆空间中开辟3个存放int型数据的空间,把1,2,3放进去,然后把内存地址值返回给变量arr.
简化写法:
int[] arr = {1,2,3};
5. 数组类型的变量直接打印的结果:
以int型数组为例:
[I@xxxx
[ :指的是变量中保存的内存空间是数组类型
I :指的是数组的类型是int型(也就是数组元素的类型)
@ :固定的分隔符
xxxx :一串字符串,十六进制.由内存地址计算而来的.
用途:比较两个数组是否是同一个数组!!(内存地址值相同,肯定是同一个数组)
二、jvm内存结构:
1.栈:
凡是方法中定义的变量(包括方法的形参)都是在栈中.
栈中的变量没有初始化值,使用之前必须手动赋值.
方法执行,变量就存在,方法执行完,变量所占用的空间自动释放!
2.堆:
凡是使用new关键字创建出来的对象都是在堆中!
堆中的变量都有初始化值:(0/0.0/false/null)
堆中的变量随着对象的存在而存在.当对象没有任何引用指向它时,它所占用的空间由垃圾回收器负责回收.
3.方法区:
.class字节码文件和方法定义
System
Scanner
Math
4.本地方法栈:
5.程序计数器:
三、数组的遍历:traversal
依次访问数组中每个元素的一种机制!
使用循环(循环的是元素的索引)就可以遍历每个元素!
循环的顺序:从前往后,从后往前.
***数组操作的两个常见异常:
1.ArrayIndexOutOfBoundsException
索引超出了正常值[0,数组的长度-1]范围:
2.NullPointerException
一个数组变量,被赋予了null值,使用它去访问数组中的元素,发生空指针异常.
作业:
1.数组初始化的两种格式:
2.什么是数组的遍历?
3.jvm虚拟机的内存划分成几个区域,各个区域中的变量都有什么特点?
4.定义一个方法,用于将一个int数组的偶数索引的元素抽取(extract)出来.
/* 定义一个方法,用于将一个int数组的偶数索引的元素抽取出来. 计算偶数索引元素的个数,创建新的数组, 遍历源数组,判断符合条件的,赋值给新数组 */ public class ExtractEvenIndexDemo{ public static void main(String[] args){ int[] arr = {1,2,3,4,5,6,7,8,9,0}; int[] dest = extractEvenIndex(arr); printArray(dest); } public static void printArray(int[] arr){ for(int i = 0;i<arr.length;i++){ System.out.print(arr[i]); if(i != arr.length - 1){ System.out.print(","); } } System.out.println(); } //返回值类型:int[] //形参列表:int[] public static int[] extractEvenIndex(int[] arr){ //计算偶数索引的个数 int[] dest = new int[(arr.length + 1) / 2]; //遍历源数组,判断 int index = 0; for(int i = 0;i<arr.length;i++){ if(i % 2 == 0){ dest[index++] = arr[i]; } } return dest; } }
5.定义一个方法,用于将一个数组逆序.注意方法的返回值类型?即:逆序是否是产生了新的数组?
/* 5.定义一个方法,用于将一个数组逆序.注意方法的返回值类型?即:逆序是否是产生了新的数组? 思路: 并没有产生新的数组,而是把源数组中的元素逆序了!! 重点:有多少次两两交换?? 4 -> 2 5 -> 2 6 -> 3 7 -> 3 ... */ public class ReverseArrayDemo{ public static void main(String[] args){ int[] arr = {1,2,3,4,5,6,7}; reverse(arr); printArray(arr); } public static void printArray(int[] arr){ for(int i = 0;i<arr.length;i++){ System.out.print(arr[i]); if(i != arr.length - 1){ System.out.print(","); } } System.out.println(); } //没有返回值:void //形参列表:int[] public static void reverse(int[] arr){ //计算两两交换的次数 int count = arr.length / 2 ; //循环交换两个元素 for(int i = 0;i<count;i++){ //交换两个元素 int temp = arr[i]; arr[i] = arr[arr.length - 1 - i]; arr[arr.length - 1 - i] = temp; } } }
6.定义一个方法,用于将一个数组的所有元素变成它原来的元素和它前一个元素的和,(第一个元素除外!)
即:若原数组为:[1,2,3,4,5]
则方法执行后,数组变为:[1,3,5,7,9]
注意:方法中不能创建新的数组!!!
/* 1.从前往后遍历:用临时变量保存当前要修改的元素的值,更改后面元素的时候,就可以使用原来元素的值. 2.从后往前遍历!用元素的值和它前一个元素的和替换当前元素! */ public class Demo1{ public static void main(String[] args){ int[] arr = {1,2,3,4,5}; printArray(arr); // change1(arr); change2(arr); printArray(arr); } public static void printArray(int[] arr){ for(int i = 0;i<arr.length;i++){ System.out.print(arr[i]); if(i != arr.length - 1){ System.out.print(","); } } System.out.println(); } //第二种方式:从后往前遍历 public static void change2(int[] arr){ // for(int i = arr.length - 1;i>0;i--){ arr[i] = arr[i] + arr[i - 1]; } } //第一种方式:使用临时变量保存上一次被覆盖的元素的值 public static void change1(int[] arr){ //定义一个变量,初始值为第一个元素的值. int temp = arr[0]; for(int i = 1;i<arr.length;i++){ //先把当前元素的值保留下来. int x = arr[i]; //替换当前元素的值 arr[i] = arr[i] + temp; //把当前元素原来的值,赋值给temp temp = x; } } }
7.定义一个方法,用于模拟裁判打分的程序:一共有5个裁判打分,去掉一个最高分,去掉一个最低分.
取剩余分数的平均值为最终的得分!!!注意:分数的数据类型!!!
重点:自定义方法之间的互相调用!!!
/* 定义一个方法,用于模拟裁判打分的程序:一共有5个裁判打分,去掉一个最高分,去掉一个最低分. 取剩余分数的平均值为最终的得分!!!注意:分数的数据类型!!! 方法之间是互相调用的关系: 自定义方法中可以使用jdk提供好的方法(比如打印方法),也可以调用自定义方法!!! */ public class MarkDemo{ public static void main(String[] args){ int[] scores = {10,20,10,15,25}; System.out.println("最终得分是: " + mark(scores)); } //double //int[] scores public static double mark(int[] scores){ //计算分数之和 // System.out.println("abc");//自定义方法中调用jdk提供的方法 int sum = getSum(scores);//自定义方法中调用自定义方法!! //减去最高分,最低分 //求平均分返回 return (sum - getMax(scores) - getMin(scores)) / 3.0; } //最高分(最大值) public static int getMax(int[] scores){ int max = scores[0]; for(int i = 1;i<scores.length;i++){ if(scores[i] > max){ max = scores[i]; } } return max; } //最低分(最小值) public static int getMin(int[] scores){ int min = scores[0]; for(int i = 1;i<scores.length;i++){ if(scores[i] < min){ min = scores[i]; } } return min; } //求数组所有元素之和 public static int getSum(int[] scores){ //定义变量,初始值为0 int sum = 0; for(int i = 0;i<scores.length;i++){ sum += scores[i]; } return sum; } }
8.定义一个方法,用于取出一个数组中所有偶数元素组成的数组!
/* 定义一个方法,用于取出一个数组中所有偶数元素组成的数组! 首先计算偶数元素的个数,用个数创建一个新的数组, 遍历源数组,把符合条件的元素赋值给新数组 两次遍历: 1.求偶数元素的个数 2.赋值 */ public class ExtractEvenElementDemo{ public static void main(String[] args){ int[] src = {1,2,3,4,5,6,67,7,8}; printArray(src); int[] dest = extractEvenElement(src); printArray(dest); } public static void printArray(int[] arr){ for(int i = 0;i<arr.length;i++){ System.out.print(arr[i]); if(i != arr.length - 1){ System.out.print(","); } } System.out.println(); } //返回值类型:int[] //形参列表:int[] src public static int[] extractEvenElement(int[] src){ //第一次遍历,求偶数元素的个数 int len = 0; for(int i = 0;i<src.length;i++){ if(src[i] % 2 == 0){ len++; } } //创建新数组 int[] dest = new int[len]; //第一次遍历,求偶数元素的个数 int index = 0; for(int i = 0;i<src.length;i++){ if(src[i] % 2 == 0){ dest[index++] = src[i]; } } return dest; } }
9.定义一个方法,用于取出一个数组中指定索引范围的元素组成的数组!!
例如:可以取出一个数组第2个到第5个索引组成的数组!
/* 定义一个方法,用于取出一个数组中指定索引范围的元素组成的数组!! 例如:可以取出一个数组第2个到第5个索引组成的数组! 思路: 根据传递进来的两个数,确定要截取的范围: 用这个范围的长度去创建一个新数组, 遍历源数组,赋值! */ public class GetArrayDemo{ public static void main(String[] args){ int[] arr = {1,2,3,4,5,6,7,8,9,0}; int[] dest = getArray(arr,2,5); printArray(dest); } public static void printArray(int[] arr){ for(int i = 0;i<arr.length;i++){ System.out.print(arr[i]); if(i != arr.length - 1){ System.out.print(","); } } System.out.println(); } //int[] //int[] arr,int start,int end public static int[] getArray(int[] arr,int start,int end){ //计算目标元素的个数 int len = end - start + 1; //创建新数组 int[] dest = new int[len]; //遍历源数组赋值 int index = 0; for(int i = start;i<=end;i++){ dest[index++] = arr[i]; } // return dest; } }
选作题:
用数组实现:产生一个有5个范围在(1-100)之间的随机数元素的数组,要求元素不能重复!!!
/* 选作题: 用数组实现:产生一个有5个范围在(1-100)之间的随机数元素的数组,要求元素不能重复!!! 思路: 先产生一个有默认值的数组, 遍历,每次都产生一个随机数,拿这个随机数到数组中判断 如果已经存在,就再次产生一个随机数,再次进行判断 如果不存在,就直接给当前的元素赋值! */ public class GetRandomArrayDemo{ public static void main(String[] args){ int[] arr = new int[5];//0 //遍历数组,产生随机数,判断,符合条件再对元素赋值 for(int i = 0;i<arr.length;i++){ int r = (int)(Math.random() * 100); //判断是否已经在数组中! /* while(exists(arr,r)){ //true ,表明已经存在,再次产生一个新的随机值! r = (int)(Math.random() * 100) + 1; } */ //优化后的方法,需要三个参数,第二个参数表明比较的范围 while(exists2(arr,i,r)){ //true ,表明已经存在,再次产生一个新的随机值! r = (int)(Math.random() * 100); } //false意味着数组中不存在这个随机值,那就直接赋值 arr[i] = r; } //查看 printArray(arr); } public static void printArray(int[] arr){ for(int i = 0;i<arr.length;i++){ System.out.print(arr[i]); if(i != arr.length - 1){ System.out.print(","); } } System.out.println(); } //优化后的方法:第二个参数表示比较的范围:只比较当前数组中已经赋值的元素!!! //优点:1.减少了比较的次数.2.避免了初始值是0值的问题!!! public static boolean exists2(int[] arr,int count,int value){ //遍历数组,比较count之前的值是否和value相等 for(int i = 0;i<count;i++){ if(arr[i] == value){ return true; } } //程序走到这里,说明for循环中没有碰到相同的数(即:value在数组中不存在) return false; } //boolean //int[] arr,int value public static boolean exists(int[] arr,int value){ //遍历数组,如果遇到相同的值,就返回true,否则返回false for(int i = 0;i<arr.length;i++){ if(arr[i] == value){ return true; } } //程序走到这里,说明for循环中没有碰到相同的数(即:value在数组中不存在) return false; } }
-----------------------------------------------------------------------new-----------------------------------------------------------------
对象
一、参数传递的原则:值传递
Java中,方法调用时,不论形参是基本数据类型还是引用数据类型,
实际上都是实参的副本传递给方法的形参!!
数组类型的参数,在方法体外能看到方法体中对数组元素的更改!!
基本数据类型,在方法体外,看不到方法体中对数据的更改!!
二、面向对象的概念:
1.面向对象编程:
OOP:object oriented programming,也就是在程序中,直接操作的是对象,而不是一个个的函数!
和此相对应的思想是:面向过程编程思想:procedual(过程化)
要实现一个功能,每一步的操作都必须自己实现.都是通过一个个的函数调用实现!
(C语言就是典型的过程化语言!)
面向对象编程,考虑的是谁有我想要实现的功能:
举例:
组装电脑:
喝咖啡:
建造汽车:
2.面向对象思想的好处:
1> 让程序员从执行者变成了指挥者
2> 更符合人类思维的编程思想!
3> 将复杂的事情简单化!
面向对象开发,实际上就是维护对象之间的关系:
例如:张三会开锁,李四会撬门,王五会开叉车.
if(张三.开锁() == ok){ 王五.开叉车(); }else if(李四.撬门() == ok){ 王五.开叉车(); }else{ 撤退!!! }
3.面向对象的三大特点:
封装,继承,多态 -- 后面详讲!!!
三、类和对象:
1.类:是对现实世界中事物的一种抽象表示!
2.现实世界中事物的描述方法:
1>静态的属性信息:颜色,价格,尺寸大小...
2>功能或者行为:run,eat...
3.Java中使用类来描述事物:
电脑: Java类:
属性信息 -> 成员变量:
功能或者行为-> 成员方法:
例如:
public class Computer{ //成员变量 String color = "black"; double price = 2000.0; int size = 16; //成员方法 public void playMusic(){...} public void playMovie(){...} }
4.类是一类事物的抽象形式:
如何来的?
应该是现实真实存在的一类事物,或者是脑海中的一个概念!
将它们共同的属性和行为抽取出来就是类!!
类一般情况下,都不能直接使用.
必须创建对象!!
从类到具体的对象.称为实例化过程(创建).
Person类定义的过程:
Dog类定义的过程:
四、类成员的写法:
1.成员变量:
和之前定义变量的规则一样
写在类中,成员方法的外边
2.成员方法:
和之前定义方法的规则一样
暂时去掉static
NoteBook.java
/* NoteBook类的定义: 一个类一般是不能直接使用的,我们使用的是类所创建的对象. 使用new关键字,就可以创建一个类的对象(实例化的过程). 一般情况下是在main方法中创建对象. main方法可以写在普通类中,或者是一个单独的测试类中. 创建对象的语法: 类名 对象名 = new 类名(); 对象的使用: 访问对象的成员变量 调用对象的成员方法 main方法和普通类没有所属关系,一般都会单独的放到一个专门的测试类中. 这个测试类主要目的就是为了放main方法.!!! */ public class NoteBook{ //成员变量 String brand = "ASUS"; int memory = 8; int price = 6000; String owner = "张三"; //成员方法 public void type(String content){ System.out.println("正在打印: " + content); } // public void playMusic(String name){ System.out.println("播放歌曲: " + name); } //显示自己的属性信息 public void showInfo(){ System.out.println("brand : " + brand + ", memory : " + memory + ", price : " + price + ", owner : " + owner); } /* //在普通类中写main方法 public static void main(String[] args){ //在这里创建对象 NoteBook book1 = new NoteBook(); //访问book1 的成员变量 System.out.println("book1.brand : "+ book1.brand); System.out.println("book1.price: " + book1.price); System.out.println("book1.memory: " + book1.memory); System.out.println("book1.owner : " + book1.owner); //调用book1的成员方法 book1.type("hello world"); book1.playMusic("<<我站在草原望北京>>"); book1.showInfo(); } */ }
五、对象的创建
格式:
类名 对象名 = new 类名();
NoteBookDemo.java
/* NoteBook类的测试类: 如果在源文件中出现了没有经过编译的源文件,那就一起编译!!! */ public class NoteBookDemo{ public static void main (String[] args){ //创建对象 NoteBook book1 = new NoteBook(); //访问book1 的成员变量 System.out.println("book1.brand : "+ book1.brand); System.out.println("book1.price: " + book1.price); System.out.println("book1.memory: " + book1.memory); System.out.println("book1.owner : " + book1.owner); //调用book1的成员方法 book1.type("hello world"); book1.playMusic("<<我站在草原望北京>>"); book1.showInfo(); } }
对象的使用:
目前,对象基本上就是两个方面可供使用:
> 成员变量的访问(access)
> 成员方法的调用(invoke/call)
- 成员之间是可以互相使用的!
- 成员变量可以使用成员方法的返回值
- 成员方法可以使用成员变量的值!
六、一个对象的内存图解:
重点:
方法区的概念
成员变量的初始化
方法区:用于存放.class字节码文件和方法定义的空间(方法并不是每个实例对象都保存一份,只是引用了方法定义的地址而已!!方法区又称为共享区!!!)
成员变量的初始化:系统的默认初始化之后,才进行显式的初始化!
七、两个对象的内存图解:
.class字节码文件只是第一次使用时加载,以后再次使用,不会多次加载!!!
不同对象有自己不同的成员变量值,但是方法都是引用方法区中的地址!!!
八、两个变量指向同一个对象图解:
重点:
多个对象名对实例对象成员变量的更改,通过其它对象名也是可以看到的.
重点:
1.方法区中存放的是程序中使用到的.class字节码文件,同一个字节码文件会在第一次使用时加载,不会多次加载.
2.不同对象中并不会保存方法的代码,只是保存了方法的地址引用而已.也就是说:方法区中的东西都是被不同的对象所共享的.方法区也称为共享区!
3.在对象的创建时,对象的成员变量首先进行默认的初始化,然后是显式的初始化(在类定义时对成员变量的赋值!).
在对象创建后,可以通过对象名.成员变量名的方式对成员变量进行更改!
Dog d = new Dog();
d.name = "abc";//如果没有显式初始化的话,name这个变量被赋值了两次:
第一次是jvm默认初始化,第二此时用对象名.变量名的方式赋值!
=============
main方法一般是单独放到一个源文件中,
为了查找类方便.合二为一啦!
把普通类定义和含main方法的测试类放到一个源文件中!!
/* 对Phone类的测试类 */ /* 自定义手机类 */ class Phone{ //成员变量 String brand; //null int price = 2000; //成员方法 public void call(String name){ System.out.println("给 " + name + " 打电话"); } // } public class PhoneDemo{ public static void main(String[] args){ //创建对象 Phone p = new Phone(); //先对成员变量赋值 p.brand = "iphone"; p.price = 8000; System.out.println("品牌: " + p.brand + ",价格: " + p.price); //调用成员方法 p.call("Obama"); } }
- 在Phone对象的创建时,对象的成员变量首先进行默认的初始化,然后是显式的初始化(在类定义时对成员变量的赋值!)
- 在堆空间中开辟一片空间,把成员变量赋值(默认的初始化值,显示初始化),
- 把对象把对象的内存的空间地址值赋值给变量p
- main方法一般单独放到一个原文件当中,为了查找方便,把普通类定义和含main方法的测试类放到一个源文件当中