基础问答
1、解释java语言的跨平台性?
与各个操作系统对应的jvm帮我们屏蔽了系统间的差异。比如我在windows环境下开发,代码放到linux环节下运行。很常见的一个跨系统操作,java代码都是同一份,就是因为win版的jvm,和Linux版的jvm发挥了作用。让我们的代码可以编译一次生成class文件,在不同的平台自由运行。
2、 JDK 和 JRE 有什么区别?
- JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。
- JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。
具体来说 JDK 其实包含了 JRE,同时还包含了编译 Java 源码的编译器 Javac,还包含了很多 Java 程序调试和分析的工具。
简单来说:如果你需要运行 Java 程序,只需安装 JRE 就可以了,如果你需要编写 Java 程序,需要安装 JDK。(大多数人安装jdk的时候都安装了jre,没有这个必要。其实可以在安装jdk的时候勾掉jre选项)
3、 何为常量?何为变量?解释 int a = 100;
常量:程序的运行过程中,内容保持不变。 例如 3,10,“学习java字符串” 诸如此类。
变量:程序的运行过程中,内容发生变化。 例如 int a ,char c 。变量使用前记得要赋初值,不然编译错误。
int a = 100; 从中划开,首先100是个常量,int a 定义了一个变量(内存中开辟了一块空间,这个空间有地址和内容(int 4个字节的范围),使用a代表这个内存空间的地址,从而来操纵这个空间的内容) 然后进行赋值,内容进行填充,换句话说,a所代表的地址空间是不变的,其中的内容是不断变化的。程序走到下一步,a = 200,或 a = 300;
4、 描述二进制(0,1)bit,字节(byte),十进制之间的关系?
计算机只能识别0和1,产生了沟通问题,后来为了方便都用字节沟通。
1个 bit 就是1位0,或者1位1 。1个字节(byte)=8个bit位(也就是8位0或1)那么有个问题8位的0和1组合那有非常多,有最大01111111和最小10000000的组合,有其范围转十进制范围为(-128~127),字节(byte)类型最终可以存放多大的数据就这样被决定出来了。字节是计算机存储的最小单元。
总结: 1byte(字节)=8bit bit是1位0或者1组成,所以8位的0或者1,十进制范围(-128~127),换句话数byte类型变量只能存放(-128-127)之间的数据。 那么,short是2个字节,16位0或1组成,int 是4个字节,32位0或者1组成。二进制转十进制,十进制转二进制这里不做讨论。win环境下,点击一个文件查看属性显示多少字节和多少KB。1KB=1024byte,1KB=1024字节。
5、a++,++a 注意事项?
单独使用: a++单独一行使用,先加后加无所谓,最终结果都是 1;
int a = 0;
a++;
System.out.println(a);// 1
非单独使用:先赋值,然后加1,打印结果为0,后加1;
int a = 0;
System.out.println(a++); //0
System.out.println(a); //1
++a:单独使用无所谓,非单独使用,则先加1后赋值。
int a = 0;
System.out.println(++a); // 1
6、if(){},if(){}else{},if(){}else if(){}else{}代码的执行?
if语句是选择结构,看条件而执行。
格式 | 执行 |
---|---|
if(){} | 有可能执行,也有可能不执行 |
if(){}else{} | 二选一,非此即彼 |
if(){}else if(){}else{} | 择其一执行 |
7、数据类型转换问题?
小类型–>大类型 不用操心,默认完成。(自动类型转换)例如:
long l = 100;// ok
整型默认为int,浮点默认为double。100是int,int型赋值给long型,自动完成。
大类型–>小类型 ,需要我们强制类型转换如:
int i = 100L; // 编译错误
int i = (int) 100L; // long型强转为 int型,可能会损失精度
注意了:代码正常这是为啥呢?
byte b = 100 // 代码ok
byte b = 128 // 代码错误
解释:编译器帮我们检查,100虽然是int,但是大小却在byte范围之内,不用强转类型转换,编译器帮我们完成,所以代码ok。如果超过byte范围,那么编译错误。
再有如下代码是错误的。
byte b = 100;
b = b+1; // 代码错误
解释:第一行代码没问题,解释如上,但是第二行代码,编译阶段是不做运算的,这个运算结果是否满足byte取值范围,不得而知,所有编译不能让其通过。任何相加减都是存在着不确定性。
8、如下是否是重载?
本类中方法名相同,参数列表不同,即方法重载,参数列表不同,包含个数和类型。
例如:如下不是重载
public void Overload(int a ,int b)
public int Overload(int c,int z)
要点: 方法名,参数列表 【和别的因素无关 】
9、String str = new String(“Hello World”); 创建了几个对象?
解释:因为有new,所以堆中必然有一个对象。另外,如果常量池中已有"Hello World",则不创建,没有,则在常量池中创建。所以这句代码,究竟在内存中创建一个还是两个对象,视情况而定。
10、判断如下代码的结果
String s1 = "a";
String s2 = s1 + "b"; // 产生一个新的字符串,在堆中。
String s3 = "a" + "b"; // 只会产生一个对象,编译器对多个字符串常量相“+”做了优化,直接放到常量池中了。
System.out.println(s2 =="ab"); //false
System.out.println(s3 == "ab"); //true
System.out.println(s2 == s3); //false
""双引号的字符串都在常量池中,多个常量相加如 String c = “a”+“b”+“c”; 等同 String c=“abc”;
切记: String str = “abc”; str是字符串引用,“abc”是字符串常量,字符串常量一旦创建是不可变的,但是str引用是可变的。
11、== 和 equals 的区别是什么?
== 解读
对于基本类型和引用类型 == 的作用效果是不同的,如下所示:
- 基本类型:比较的是值是否相同;
- 引用类型:比较的是引用是否相同;(判断对象是否是同一个)
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true
代码解读:因为 x 和 y 指向的是同一个引用,所以 == 也是 true,而 new String()方法则重写开辟了内存空间,所以 == 结果为 false,而 equals 比较的是值,所以结果都为 true。
equals 解读
equals 本质上就是 ==,Object中的equals比较的就是引用值,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。
首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:
class Cat {
public Cat(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Cat c1 = new Cat("咪咪");
Cat c2 = new Cat("咪咪");
System.out.println(c1.equals(c2)); // false
输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下:
public boolean equals(Object obj) {
return (this == obj);
}
原来 equals 本质上就是 ==。
那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:
String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true
同样的,当我们进入 String 的 equals 方法,找到了答案,代码如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。
总结 : == 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。
11、Java 中的 Math. round(-1. 5) =?Math. ceil(-11.2) =?Math. floor(-11.2) =?
等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。四舍五入
等于-11.0,向上取整。
等于-12.0,向下取整。
记住数轴的样子,从左到右,越来越大。
12、简述static的作用和你的一些思索?
-
static修饰成员变量主要实现对象间共享数据的目的,静态成员和静态方法都是隶属于类的特征。
-
静态方法只能访问静态成员,实例方法可以使用静态和非静态成员,因为,静态先存在内存中,非静态后存在内存。一般访问需要用类名点 或者实例点 的方式访问,以免混淆将静态成员变量当成普通成员变量,推荐使用类名点的方式。静态方法不能使用this关键字。
-
静态代码块,优先于main()和构造方法,主要作用是初始化静态成员。
-
static修饰的内容(成员+方法+代码块)随类的加载而加载,仅加载一次,存储在方法区中的静态区。
13、谈谈继承?
继承解决共性抽取的问题,联想static,static是对象公用一份儿,继承是你有我有大家有。共性的代码抽取到父类中,子类只需要继承即可获得。
从三个方面阐述:继承对成员变量,成员方法,构造器的影响
成员变量
-
变量不重名
子类父类中出现不重名的成员变量,这时的调用是没有影响的 -
变量重名
直接通过子类对象访问成员变量:Zi zi = new Zi(); zi.num;
等号左边是谁,就优先用谁,没有则向上找。
间接通过成员方法访问成员变量:zi.methodZi(); zi.methodFu();
该方法属于谁(方法要是重名请看下面),就优先用谁的成员,没有则向上找。
public class Fu { //父类
int num = 100;
public void methodFu() {
// 使用的是本类当中的,不会向下找子类的
System.out.println(num);
}
}
public class Zi extends Fu{ // 子类
int num = 200;
public void methodZi() {
// 因为本类当中有num,所以这里用的是本类的num
System.out.println(num);
}
}
public class Hello {
public static void main(String[] args) {
Zi zi = new Zi();
System.out.println("zi.num="+zi.num); // 200 普通调用,对象.成员变量 看左边(Zi zi)
System.out.println("===========");
// 这个方法是子类的,优先用子类的,没有再向上找
zi.methodZi(); // 200
// 这个方法是在父类当中定义的,
zi.methodFu(); // 100
}
}
成员方法
-
方法不重名:
Zi zi = new Zi(); zi.method();
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。 -
方法重名(特例重写)
概念:在继承关系当中,方法的名称一样,参数列表也一样。
重写(Override):方法的名称一样,参数列表【也一样】。覆盖、覆写。
重载(Overload):方法的名称一样,参数列表【不一样】。
方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。
- 创建的对象是谁(new),就优先用谁的方法,如果没有则向上找。
- 无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。
方法覆盖重写的注意事项:
1. 必须保证父子类之间方法的名称相同,参数列表也相同。
@Override:写在方法前面,用来检测是不是有效的正确覆盖重写。
这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。
2. 子类方法的返回值必须【小于等于】父类方法的返回值范围。
小扩展提示:java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是Object的子类。
3. 子类方法的权限必须【大于等于】父类方法的权限修饰符。
小扩展提示:public > protected > (default) > private
构造器
public class Fu { //父类
public Fu() {
System.out.println("父类无参构造");
}
public Fu(int num) {
System.out.println("父类有参构造!");
}
}
public class Zi extends Fu {
public Zi() {
super(); // 在调用父类无参构造方法
// super(20); // 在调用父类重载的构造方法
System.out.println("子类构造方法!");
}
public void method() {
// super(); // 错误写法!只有子类构造方法,才能调用父类构造方法。
}
}
/*
继承关系中,父子类构造方法的访问特点:
1. 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。
2. 子类构造可以通过super关键字来调用父类重载构造。
3. super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。
总结:
子类必须调用父类构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个。
*/
/*
super关键字的用法有三种:
1. 在子类的成员方法中,访问父类的成员变量。
2. 在子类的成员方法中,访问父类的成员方法。
3. 在子类的构造方法中,访问父类的构造方法。
*/
public class Zi extends Fu {
int num = 20;
public Zi() {
super();
}
public void methodZi() {
System.out.println(super.num); // 父类中的num
}
public void method() {
super.method(); // 访问父类中的method
System.out.println("子类方法");
}
}
super关键字用来访问父类内容,而this关键字用来访问本对象内容。
A. this(…)调用也必须是构造方法的第一个语句,唯1一个。
B. super和this两种构造调用,不能同时使用。
public void showNum() {
int num = 10;
System.out.println(num); // 局部变量
System.out.println(this.num); // 本类中的成员变量
System.out.println(super.num); // 父类中的成员变量
}
14、既然有了普通类,为何还需要抽象类?是否多余了?
解释:抽象类还是隶属于类,从名字就可以看出。举例,假如我们有一个动物类,有一个共性行为,吃东西。吃东西这个行为无法准确描述,动物们的吃东西方式各自不同。从而方法的方法体是不能确定的。给这样的类起个名字叫抽象类。同时也是抽象类无法直接实例的原因。
public abstract class Abs{
public abstract void eat();
}