文章目录
java
java基础
java在DOS中编译和运行
-
带包编译:
文件根目录 javac 文件名.java
- 例子:javac HelloWorld.java
-
带包运行:
包根目录 java 包名.类名 参数1 参数2
- 例子:java com.sean.test.HelloWorld arg1 arg2
-
jar包运行:
java -jar jar包目录\jar包名称.jar 参数1 参数2
- 例子:java -jar E:\jar\util.jar arg1 arg2
关键字
- 不常用字段解释
- strictfp:在接口和类上加上该标识,会对float和double进行严格计算
- transient:在对象成员变量中加上该标识,在对象序列化时,被标识变量不会被序列化
- volatile:在变量前加入该标示,解决多线程情况下变量的可见性和有序性,但无法解决原子性问题,应用场景为,变量的值不参与修改的情况。参见:Java并发编程:volatile关键字解析
this
作用:
- 解决了局部变量隐藏成员变量的问题
static关键字
- 特点:
1.1 随着类的加载而加载
1.2 优先于对象存在
1.3 被类的所有对象共享
1.4 既可以通过类名调用,也可以通过对象名调用 - 内存图
静态的内容在方法区的静态区 - 注意事项
3.1 静态方法中没有this对象
3.2 静态只能访问静态
3.3 修饰类时,只能修饰内部类,修饰内部类的好处是可以不通过创建外部类对象就可创建内部类对象
final关键字
(1)是最终的意思,可以修饰类,方法,变量。
(2)特点:
A:它修饰的类,不能被继承。(主要的场景为:不想该类中的方法被扩展,数据不被修改,如Integer、String)
B:它修饰的方法,不能被重写。
C:它修饰的变量,是一个常量。
(3)面试相关:
A:局部变量
a:基本类型 值不能发生改变
b:引用类型 地址值不能发生改变,但是对象的内容是可以改变的
B:初始化时机
a:只能初始化一次。
b:常见的给值
定义的时候。(推荐)
或者静态代码块中。
构造方法中。
注意事项:
类名前
不使用:
private、protected、static
使用:
权限修饰符:默认修饰符,public
状态修饰符:final
抽象修饰符:abstract
成员变量
不使用:
abstract
使用
权限修饰符:private,默认的,protected,public
状态修饰符:static,final
数据类型
-
基本数据类型
A:整数 占用字节数 byte 1 short 2 int 4 long 8 B:浮点数 float 4 double 8 C:字符 char 2 D:布尔 boolean 1 注意:!!!!! 整数默认是int类型,浮点数默认是double。 长整数要加L或者l。 单精度的浮点数要加F或者f。
思考题和面试题
-
下面两种方式有区别吗?
float f1 = 12.345f; // 本身是一个float类型 float f2 = (float)12.345; // 开始是一个double类型,强转为float类型
-
下面的程序有问题吗,如果有,在哪里呢?
byte b1 = 3; byte b2 = 4; byte b3 = b1 + b2; // 有问题,byte参与运算就转换为int类型,编译会报错 byte b4 = 3 + 4; // 常量,先把结果计算出来,然后看是否在byte的范围内,如果在,就不报错
-
下面的操作结果是什么呢?
byte b = 130; // 编译报错,130不在范围内 byte b = (byte)130; // -126 源码反码补码!???
-
字符参与运算
是查找ASCII里面的值 'a' 97 'A' 65 '0' 48 System.out.println('a'); // a System.out.println('a' + 1); // 98
-
字符串参与运算
这里其实是字符串的连接
System.out.println("hello"+'a'+1); // helloa1 System.out.println('a'+1+"hello"); // 98hello System.out.println("5+5="+5+5); // 5+5=55 System.out.println(5+5+"=5+5"); // 10=5+5
字符串
面试题1:
String s = new String(“hello”);和String s = "hello"的区别?
前者有两个地址值,一个是在方法区的常量的地址值,另一个是new String()的地址值,有两个对象
后者是直接赋值,到方法区去找他的地址只有一个对象
面试题2:
String s1 = “hello”;
String s2 = “world”;
String s3 = “helloworld”;
System.out.println(s3 == s1 + s2);// false s1+s2会在堆中新建一个地址,地址值不相同(+会重新new一个对象)
System.out.println(s3.equals((s1 + s2)));// true
System.out.println(s3 == "hello" + "world");// false 这个我们错了,应该是true
System.out.println(s3.equals("hello" + "world"));// true
面试题3:
String,StringBuffer,StringBuilder的区别
String: 内容是不可变的
StringBuffer: 是一个容器,内容是可变的,同步的,线程安全,效率低
StringBuilder: 也是一个容器,内容是可变的,不同步的,线程不安全,效率高
面试题4:
StringBuffer是如何实现线程安全的:
除构造方法外和部分insert和indexOf方法外,都使用了在方法上加同步锁的方式保证线程安全,而未加锁的insert和indexOf方法是通过调用父类,父类再调用其本身的已加锁的方法保证线程安全。
Integer
面试题1:
-128到127之间的数据缓冲池问题
但特殊和要注意的是: 若传入的是 -128~127 的值,则不是引用类型,而是类似
于基本类型的常量池中的值,故而可以用 == 号去判断
即: new Integer(127)==new Integer(127); //true
运算符
赋值运算符
扩展的赋值运算符的特点
隐含了自动强制转换。
面试题:
short s = 1;
s = s + 1;(编译错误)
short s = 1;
s += 1;(<==>s=(short)(s+1))
请问上面的代码哪个有问题?
位运算
位运算符 | 说明 |
---|---|
>> | 右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,若为正数则高位补0,若为负数则高位补1 |
<< | 左移运算符,符号左侧数值 按位左移 符号右侧数值指定的位数,并在低位处补0 |
>>> | 无符号右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,无论正负高位补0 |
& | 与(AND)运算符,对两个整型操作数中对应位执行布尔代数,两个位都为1时输出1,否则0 |
| | 或(OR)运算符,对两个整型操作数中对应位执行布尔代数,两个位中只要有一个为1就输出1,否则为0 |
^ | 异或(XOR)运算符,对两个整型操作数中对应位执行布尔代数,两个位相等则为0,不相等则为1 |
~ | 非(NOT)运算符,按位取反运算符翻转操作数的每一位,即0变成1,1变成0 |
方法
java内存分配
-
栈:存储局部变量(在方法定义中或者方法申明上定义的变量)以及常量值
数据使用完毕就消失
-
堆:存储所有new出来的对象
每一个new出来的东西都有地址 每一个变量都有默认值 byte,short,int,long 0 float,double 0.0 char '\u0000' boolean false 引用类型 null 数据使用完毕后,在垃圾回收器空闲的时候回收。
-
方法区:
class内容区域:文件中的内容,包含成员变量和成员方法,每个class的方法会有一个地址,为堆中的调用提供连接 静态区:静态变量和静态方法
-
本地方法去:与本地系统相关
-
寄存器:CPU相关
创建对象,内存的流程图:
(1)把Student.class文件加载到内存
(2)在栈内存为s开辟空间
(3)在堆内存为学生对象申请空间
(4)给学生的成员变量进行默认初始化。null,0
(5)给学生的成员变量进行显示初始化。林青霞,27
(6)通过构造方法给成员变量进行初始化。刘意,30
(7)对象构造完毕,把地址赋值给s变量
成员变量和局部变量区别
- 在类中的位置
成员变量:类中,方法外
局部变量:在方法中或方法声明上 - 在内存中的位置
成员变量:堆
局部变量:栈 - 初始化的值
成员变量:有默认值
局部变量:没有默认值,只有定义,赋值,才能使用 - 生命周期
成员变量:随着对象的创建而创建,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的结束而消失
构造方法
- 格式:
- 方法名和类名相同
- 没有返回值类型,void也没有
- 没有返回值
思考题:构造方法中可不可以有return语句呢? *****
可以。而是我们写成这个样子就OK了:return;
其实,在任何的void类型的方法的最后你都可以写上:return;
代码块执行顺序
看程序写结果:
A:一个类的静态代码块,构造代码块,构造方法的执行流程
静态代码块 > 构造代码块 = 显示初始化 (看顺序) > 构造方法
B:静态的内容是随着类的加载而加载
静态代码块的内容会优先执行
C:子类初始化之前先会进行父类的初始化
注: 类成员变量的初始化顺序:显式初始化与构造代码块的初始化等级一致,故由代码顺序决定初始化顺序,但注意的是构造代码块不能加数据类型
结果是:
静态代码块Fu
静态代码块Zi
构造代码块Fu
构造方法Fu
构造代码块Zi
构造方法Zi
class Fu {
static {
System.out.println("静态代码块Fu");
}
{
System.out.println("构造代码块Fu");
}
public Fu() {
System.out.println("构造方法Fu");
}
}
class Zi extends Fu {
static {
System.out.println("静态代码块Zi");
}
{
System.out.println("构造代码块Zi");
}
public Zi() {
System.out.println("构造方法Zi");
}
}
class ExtendsTest2 {
public static void main(String[] args) {
Zi z = new Zi();
}
}
方法重写和方法重载的区别
- 方法重写:方法名,参数和返回值都相同
- 方法重载:方法名相同,参数不同,与返回值无关
继承
继承的好处
A:提高了代码的复用性
B:提高了代码的维护性
C:让类与类产生了一个关系,是多态的前提
继承的弊端
A:让类的耦合性增强。这样某个类的改变,就会影响其他和该类相关的类。
原则:低耦合,高内聚。
耦合:类与类的关系
内聚:自己完成某件事情的能力
B:打破了封装性
继承的成员关系
- A:成员变量
- a:子类的成员变量名称和父类中的成员变量名称不一样,这个太简单
- b:子类的成员变量名称和父类中的成员变量名称一样,这个怎么访问呢?
- 子类的方法访问变量的查找顺序:
- 在子类方法的局部范围找,有就使用。
- 在子类的成员范围找,有就使用。
- 在父类的成员范围找,有就使用。
- 找不到,就报错。
- 子类的方法访问变量的查找顺序:
- B:构造方法
-
a:子类的构造方法默认会去访问父类的无参构造方法
- 是为了子类访问父类数据的初始化
-
b:父类中如果没有无参构造方法,怎么办?
- 子类通过super去明确调用带参构造
- 子类通过this调用本身的其他构造,但是一定会有一个去访问了父类的构造
- 让父类提供无参构造
注意事项:
this(…)或者super(…)必须出现在第一条语句上。
如果不是放在第一条语句上,就可能对父类的数据进行了多次初始化,所以必须放在第一条语句上。
-
多态
多态中的成员访问特点
A:成员变量
编译看左边,运行看左边
B:构造方法
子类的构造都会默认访问父类构造
C:成员方法
编译看左边,运行看右边
D:静态方法
编译看左边,运行看左边
(静态和类相关,算不上重写,所以,访问还是左边的)
多态继承中的内存图解
多态中的对象变化内存图解
面试题
class A {
public void show() {
show2();
}
public void show2() {
System.out.println("我");
}
}
class B extends A {
/*
public void show() {
show2();
}
*/
public void show2() {
System.out.println("爱");
}
}
class C extends B {
public void show() {
super.show();
}
public void show2() {
System.out.println("你");
}
}
public class DuoTaiTest4 {
public static void main(String[] args) {
A a = new B();
a.show();// 爱
B b = new C();
b.show();// 你
}
}
抽象类
抽象类的特点
A:抽象类和抽象方法必须用关键字abstract修饰
B:抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类
C:抽象类不能实例化
D:抽象类的子类
a:是一个抽象类。
b:是一个具体类。这个类必须重写抽象类中的所有抽象方法。
抽象类的问题
A:抽象类有构造方法,不能实例化,那么构造方法有什么用?
用于子类访问父类数据的初始化
B:一个类如果没有抽象方法,却定义为了抽象类,有什么用?
为了不让创建对象
C:abstract不能和哪些关键字共存
a:final 冲突
b:private 冲突
c:static 无意义
接口
接口的成员特点
A:成员变量
只能是常量
默认修饰符:public static final
B:构造方法
没有构造方法
C:成员方法
只能是抽象的
默认修饰符:public abstract
类与类,类与接口,接口与接口
A:类与类
继承关系,只能单继承,可以多层继承
B:类与接口
实现关系,可以单实现,也可以多实现。
还可以在继承一个类的同时,实现多个接口
C:接口与接口
继承关系,可以单继承,也可以多继承
内部类
内部类访问规则
A:可以直接访问外部类的成员,包括私有
B:外部类要想访问内部类成员,必须创建对象
成员内部类
- 成员内部类不是静态的:
外部类名.内部类名 对象名 = new 外部类名.new 内部类名(); - 成员内部类是静态的:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
30,20,10
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public viod show() {
int num = 30;
System.out.println(num);
System.out.println(this.num);
System.out.println(Outer.this.num);
}
}
}
局部内部类
A:局部内部类访问局部变量必须加final修饰。
B:为什么呢?
因为局部变量使用完毕就消失,而堆内存的数据并不会立即消失。
所以,堆内存还是用该变量,而该变量已经没有了。
为了让该值还存在,就加final修饰。
C:匿名内部类是局部内部类的简化心事
D:匿名内部类的面试题(补齐代码)
interface Inter {
void show();
}
class Outer {
//补齐代码
public static Inter method() {
return new Inter() {
public void show() {
System.out.println("HelloWorld");
}
};
}
}
class OuterDemo {
public static void main(String[] args) {
Outer.method().show(); //"HelloWorld"
}
}
复制
浅复制:
将基本数据类型的值进行复制和引用类型的地址进行复制
深复制:
将基本类型的值和引用类型的值进行复制