1.方法相关问题
public class Main {
static void changeStr(String x) {
x = "xyz";
}
static void changeArr(String[] strs) {
for (int i = 0; i < strs.length; i++) {
strs[i] = strs[i]+""+i;
}
}
public static void main(String[] args) {
String x = "abc";
changeStr(x);
System.out.println(x);
changeArr(args);
System.out.println(Arrays.toString(args));
}
}
1.1 changeStr与changeArr的功能各是什么?
①修改字符串的内容为"xyz"
②修改字符串数组的内容strs[i]为"strs[i]i",即在原来的数组内容的后面加上
1.2 main方法的x有没有被改变?为什么?
没有,因为该方法的类型为void,并没有把改变后的x返回
1.3 main方法的args数组的内容有没有被改变?为什么?
没有,因为该数组没有初始化
1.4 args数组中的值是从哪里来的?要怎么才能给他赋值
输出数组会出现[]是因为Arrays.toString()这个方法会打印数组的内容,例如当数组里有1,2,3时,会以[1,2,3]的形式打印,所以当数组为空的时候就只会打印[]
数组的初始化分为静态初始化和动态初始化
静态初始化:
完整格式:
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,元素4…};
简化格式:
数据类型[] 数组名 = {元素1,元素2,元素3,元素4…};
注意点:
等号前后的数据类型必须保持一致。
数组一旦创建之后,长度不能发生变化。
动态初始化:
格式:数据类型[] 数组名 = new 数据类型[数组的长度];
该方法可以配合for循环进行给数组赋值
2. 数组相关问题
int[] arr = new int[3];
arr[0] = 1; arr[1] = 1;
int[] arrX = arr;
arr[0] = 2;
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arrX));
2.1输出结果是什么?为什么?
输出结果:
[2, 1, 0]
[2, 1, 0]
使用数组的动态初始化会默认给数组赋值,所以会出现arr[3]的值为0
数组的默认初始化值:
整数类型:0
小数类型:0.0
布尔类型:false
字符类型:‘\u0000’
引用类型:null
String[] strArr = {"aa","bb","cc"};
strArr[1] = "xx";
System.out.println(Arrays.toString(strArr));
2.2 字符串是不可变类,为什么可以对strArr[1]赋值"xx"?
String类对象内容不能修改,但并不代表其引用不能改变
3. 使用int[5][] 定义一个二维数组,其第二维到底有多长?尝试补全代码,然后使用foreach获其他循环方法遍历这个二维数组?
第二维的长度需要自己去定义,并且每个一维数组都需要初始化
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[][] arr = new int[5][];
for (int i = 0; i < arr.length; i++) {
arr[i] = new int[sc.nextInt()];//一维数组初始化
for (int j = 0; j < arr[i].length; j++) {
arr[i][j] = sc.nextInt();
}
}
for (int[] ints : arr) {
for (int anInt : ints) {
System.out.print(anInt);
}
System.out.println();
}
}
}
测试数据:1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 1 2 3 4 5
运行结果:
1
12
123
1234
12345
4.类与对象的区别是什么? Math类有对象吗?String类有什么属性是private的,有什么方法是public的,为什么这样设计(尝试举两例说明)?
区别:
-
类是一个抽象的概念,它不存在于现实中的时间/空间里,类只是为所有的对象定义了抽象的属性与行为。就好像“Person(人)”这个类,它虽然可以包含很多个体,但它本身不存在于现实世界上
-
对象是类的一个具体。它是一个实实在在存在的东西
-
类是一个静态的概念,类本身不携带任何数据。当没有为类创建任何对象时,类本身不存在于内存空间中
-
对象是一个动态的概念。每一个对象都存在着有别于其它对象的属于自己的独特的属性和行为。对象的属性可以随着它自己的行为而发生改变
Math是一个工具类,构造器是private私有的,类里面的方法都是静态方法,所以没有对象
String:
私有属性:
- final byte[] value
- final byte coder
- int hash
- boolean hashIsZero
公开方法: - charAt(int index)
- equals(Object anObject)
那为什么要这样设计呢?
其实好处就是原因,String 设计成不可变,主要是从性能和安全两方面考虑
String 类设计成不可变的原因:
- 常量池的需要
- hashcode缓存的需要
因为字符串不可变,所以在它创建的时候 hashcode 就被缓存了,不需要重新计算。这就使得字符串很适合作为 HashMap 中的 key,效率大大提高 - 多线程安全
那是如何实现不可变的? - 私有成员变量
- Public的方法都是复制一份数据,String 有很多 public 方法,每个方法都将创建新的 String 对象
- String 是 final 的,String 被 final 修饰,因此我们不可以继承 String,因此就不能通过继承来重写一些方法
- 构造函数深拷贝
从 String 类的设计方式,我们可以总结出实现不可变类的方法:
将 class 自身声明为 final,这样别人就不能通过扩展来绕过限制了。
将所有成员变量定义为 private 和 final,并且不要实现 setter 方法。
通过构造对象时,成员变量使用深拷贝来初始化,而不是直接赋值,这是一种防御措施,因为你无法确定输入对象不被其他人修改。
如果确实需要 getter 方法,或者其他可能返回内部状态的方法,使用 copy-on-write 原则,创建私有的 copy。
5. 将类的属性设置为public可以方便其他类访问,但为什么Java中普遍使用setter/getter模式对对象的属性进行访问呢?这与封装性又有什么关系?
因为将属性私有化再提供setter/getter方法来设置类的属性的话只有我们直接允许别人修改的属性才可以被修改,其他类通过(引用.变量)的方式访问不了了,必须通过(引用.方法)来访问,而方法里面可以写限制的条件,这样就保证了安全性,即与封装性的关系
6. 对象的属性可在什么时候进行初始化?都有哪些进行初始化的办法?
在你需要使用这些属性的时候进行初始化即可
进行初始化的办法:
- 声明该属性的时候初始化
- 构造方法中初始化
- 初始化块
7. 尝试使用作用域来说明封装性
将类的成员属性进行私有化,使得该成员属性只能在本类中能够被使用,即这些私有的成员属性的作用域只在本类,我们利用成员属性私有化的方式可以把我们不需要对外提供的内容都隐藏起来,这也是利用了一个作用域的提现