作为复习,前面基础部分只挑一些重点记录了
数组
- 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括
基本数据类型和引用数据类型。 - 创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是 这块连续空间的首地址。
- 数组的长度一旦确定,就不能修改。
数组的使用
初始化
- 动态初始化:数组声明 与 赋值操作 分开进行
int[] arr = new int[2];
arr[0] = 1;
arr[1] = 2;
- 静态初始化:在定义数组的同时就为数组元素赋值
int[] arr = new int[]{1, 2};
默认初始化
- 数组是引用类型,它的元素相当于类的成员变量,因此数组一经分配空间,其中的每个元素也被按照成员变量同样的方式被隐式初始化,默认初始值为0.
- 对于基本数据类型,默认初始化值为0(0.0、false之类的)
- 对于引用数据类型,默认初始化值为null
数组内存解析
int[] s
s = new int[10]
- 栈用来存放局部变量,比如在方法(比如main方法,各种自定义方法)中定义的变量都是局部变量
- String存放在常量池,且常量池中不能有相同的字符串(言外之意,字符串相同的String指向同一个地址)
- static存放在静态域
int[] ages
在栈中创建了ages
变量,指定它指向int[]
对象;new int[4]
在堆中创建了长度为4的整型数组;默认初始化值为0;int[] ages = new int[4]
把数组首地址值赋给ages
;ages[0] = 12
等三个句子,分别修改对应的数组元素内容String[] names
在栈中创建了names
变量,指定它指向String[]
对象;new String[]{"赵宇", "张凯", "江运", "曹林"}
在堆中创建长度为4的字符串数组,默认初始化为null
,紧接着对每个数组元素的内容进行修改。然后把数组首地址值赋给names
;new String[]{"Tom", "Jerry"}
创建并赋值了一个新的数组,names = new String[]{"Tom", "Jerry"}
把新数组首地址值赋给names
,覆盖了原地址;原来的数组此时没有变量指向,被丢弃,后续会有垃圾回收机制进行空间回收
多维数组
- 对于二维数组的理解,我们可以看成是一维数组 array1又作为另一个一维数组array2的元素而存 在。其实,从数组底层的运行机制来看,其实没有多维数组
多维数组的使用
- 和一维数组一样,有动态初始化和静态初始化
- 初始化时可以不指定第二维数组的长度,第二维数组每一个后续可以赋予不同的长度,例如:
int[][] arr = new int[2][];
arr[0] = new int[3];
arr[1] = new int[2];
- 但是诸如
int[][] arr = new int[][2]
这种创建形式是非法的。
特殊写法:int[] x, y[]:x是一维数组,y是二维数组
附:
声明:int[] x,y[]; 在给x,y变量赋值以后,以下选项允许通过编译的是:
a) x[0]=y; //no。左为单个元素右为二维数组
b) y[0] = x; //yes。左为一维数组右为一维数组
c) y[0][0] = x; no
d) x[0][0] = y; //no。左边本身就错的
e) y[0][0] = x[0]; yes
f ) x = y; no
以上就看等号左右是不是同类型的对象,是就可以赋值,不是就不可以。
多维数组内存解析
Arrays工具类
1 | boolean equals(int[] a, int[] b) | 判断两个数组是否相等 |
2 | String toString(int[] a) | 以字符串输出数组 |
3 | void fill(int[] a, int val) | 将指定值填充到数组之中 |
4 | void sort(int[] a) | 对数组进行排序 |
5 | int binarySearch(int[] a, int key) | 对排序后的数组进行二分查找指定的值 |
数组常见异常
- 数组脚标越界异常:
ArrayIndexOutOfBoundsException
- 空指针异常:
NullPointerException
面向对象
- 三大特性:封装、继承、多态
对象
对象的内存解析
- 堆(Heap),此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在 Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
- 通常所说的栈(Stack),是指虚拟机栈。虚拟机栈用于存储局部变量等。 局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、 char 、 short 、 int 、 float 、 long 、 double)、对象引用(reference类型, 它不等同于对象本身,是对象在堆内存的首地址)。 方法执行完,自动释放。
- 方法区(Method Area),用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
对象数组的内存解析
方法
变量的分类
- 变量分为
- 在方法体外,类体内生命的变量称为成员变量
- 实例变量(不以static修饰)
- 类变量(以static修饰)
- 在方法体内声明的变量称为局部变量
- 形参(方法、构造器中定义的变量)
- 方法局部变量(在方法内定义)
- 代码块局部变量(在代码块内定义)
- 在方法体外,类体内生命的变量称为成员变量
- 内存加载位置:
- 成员变量:堆空间或静态域内
- 局部变量:栈空间
方法的重载
- 重载的概念:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
- 重载的特点:
与返回值类型无关,只看参数列表
,且参数列表必须不同。(参数个数或参数类 型)。调用时,根据方法参数列表的不同来区别。
方法的重写
这是出现在继承里的一个概念,指在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限(子类不能重写父类中声明为
private
权限的方法。不过写了也不报错,但此时不是重写了,各用各的方法。因为子类根本“看不见”父类的private
方法,所以也不存在“重写”) - 子类方法抛出的异常不能大于父类被重写方法的异常
注:子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为 static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的static方法。
可变个数的形参
- JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books);
- JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String...books);
一个方法最多只能声明一个可变个数的形参,并且要放在形参声明的最后
方法参数的值传递机制(重点!!!!!!!)
-
方法,必须由其所在类或对象调用才有意义。若方法含有参数:
- 形参:方法声明时的参数
- 实参:方法调用时实际传给形参的参数值
-
Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本 (复制品)传入方法内,而参数本身不受影响。
- 若形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
- 若形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
-
基本数据类型的参数传递:
-
引用数据类型的参数传递:
public static void main(String[] args) {
Person obj = new Person();
obj.age = 5;
System.out.println("修改之前age = " + obj.age);// 5 // x是实参
change(obj);
System.out.println("修改之后age = " + obj.age);// 3
}
public static void change(Person obj) {
System.out.println("change:修改之前age = " + obj.age);
obj.age = 3;
System.out.println("change:修改之后age = " + obj.age);
}
class Person{
int age;
}
public static void main(String[] args) {
Person obj = new Person();
obj.age = 5;
System.out.println("修改之前age = " + obj.age);// 5 // x是实参
change(obj);
System.out.println("修改之后age = " + obj.age);// 5
}
public static void change(Person obj) {
obj = new Person(); //创建了新对象
System.out.println("change:修改之前age = " + obj.age);
obj.age = 3;//指向的是新对象的age
System.out.println("change:修改之后age = " + obj.age);
}
class Person{
int age;
}
public class TransferTest3 {
public static void main(String args[]) {
TransferTest3 test = new TransferTest3();
test.first();
}
public void first() {
int i = 5;
Value1 v = new Value1();
v.i = 25;
second(v, i);
System.out.println("first:" + v.i + " " + i);
}
public void second(Value1 v, int i) {
i = 0;
v.i = 20;
Value1 val = new Value1();
v = val;
System.out.println("second:" + v.i + " " + i);
}
}
class Value1 {
int i = 15;
}
- 思考题1
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 10;
method(a, b);
System.out.println("a = " + a);
System.out.println("b = " + b);
//需要只打印出a = 100, b = 200.请写出method方法的代码
}
}
思路一:重写输出流
public static void method(int a, int b){
PrintStream ps = new PrintStream(System.out){
@Override
public void println(String x){
if("a = 10".equals(x)){
x = "a = 100";
}else if ("b = 10".equals(x)){
x = "b = 200";
}
super.println(x);
}
};
System.setOut(ps);
}
思路二:不执行后面的System.out.println
:
public static void method(int a, int b){
a = a * 10;
b = b * 20;
System.out.println(a);
System.out.println(b);
System.exit(0);
}
- 思考题2:
定义一个int型的数组:int[] arr = new int[]{12,3,3,34,56,77,432}
; 让数组的每个位置上的值去除以首位置的元素,得到的结果,作为该位置上的 新值。遍历新的数组。
错误写法:
for(int i= 0;i < arr.length;i++){
arr[i] = arr[i] / arr[0];
}
正确写法一:
for(int i = arr.length – 1;i >= 0;i--){
arr[i] = arr[i] / arr[0];
}
正确写法二:
int temp = arr[0];
for(int i= 0;i < arr.length;i++){
arr[i] = arr[i] / temp;
}
附:遇到递归方法想不清的,可以用下图的方式思考一下