学习Java,首先要知道Java是一门面向对象编程的语言,而我们之前学过C++,C++是C语言的继承,C++既可以进行C语言的过程化程序设计,又可以进行面向对象的程序设计,那么问题来了,面向对象与面向过程分别代表什么含义呢?
面向过程:
面向对象
面向对象与面向过程的区别
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定;
即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
在 Java 中有两种形式可以实现多态:
方法一:
String cats[] = new String[] {
"Tom","Sam","Mimi"
};
方法二:
String dogs[] = {"Jimmy","Gougou","Doggy"};
注意:声明一个数组不需分配任何存储空间,它仅仅是代表你试图创建一个数组。
跟C/C++声明一个数组的明显区别就是空间的大小没有被特别标识。
因此,下面的声明将会引起一个编译期错误。
int num[5];
即:上面代码Demo中静态初始化中的方法二,如果改为String dogs[3] = {"Jimmy","Gougou","Doggy"}; 则在编译时报错。
一个数组的大小将在数组使用new关键字真正创建时被给定,例如:
int num[];
num = new int[5];
JVM
JDK 和 JRE
注释: 如果你只是为了运行一下 Java 程序的话,那么你只需要安装 JRE 就可以了。 如果你需要进行一些 Java 编程方面的工作,那么你就需要安装 JDK 了。 但是,这不是绝对的。有时,即使您不打算在计算机上进行任何 Java 开发,仍然需要安装 JDK。 例如,如果要使用 JSP 部署 Web 应用程序,那么从技术上讲,您只是在应用程序服务器中运行 Java 程序。 那你为什么需要 JDK 呢? 因为应用程序服务器会将 JSP 转换为 Java servlet,并且需要使用 JDK 来编译 servlet。
构造器 Constructor(构造函数) 是否可被 override?
String、StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?
自动装箱与拆箱
基本数据类型对应的包装器类型看:
我们就以Interger类为例,下面看一段代码:
public class Main {
public static void main(String[] args) {
Integer i = 10; //装箱
int n = i; //拆箱
}
}
复制代码
反编译class文件之后得到内容:
从反编译得到的字节码内容可以看出:
面试题:
下面这段代码的输出结果是什么?
public class Main {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
复制代码
}
输出结果是: true false
解析: 在通过valueOf方法创建Integer对象的时候, 如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;
否则创建一个新的Integer对象。
注释:不同数据类型valueOf方法实现不同,其中:
在一个静态方法内调用一个非静态成员为什么是非法的?
静态方法与非静态方法的一些注意:
在 Java 中定义一个不做事且没有参数的构造方法的作用
接口和抽象类的区别是什么?
jdk7~jdk9 Java 中接口概念的变化
成员变量与局部变量的区别有哪些?
创建一个对象用什么运算符?对象实体与对象引用有何不同?
什么是方法的返回值?返回值在类的方法里的作用是什么?
一个类的构造方法的作用是什么? 若一个类没有声明构造方法,该程序能正确执行吗? 为什么?
在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
== 与 equals(重要)
== :
equals() :
举个例子:
public class test1 {
public static void main(String[] args) {
String a = new String("ab"); // a 为一个引用
String b = new String("ab"); // b为另一个引用,对象的内容一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一对象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("yep");
if (42 == 42.0) { // true
System.out.println("true");
}
}
}
复制代码
说明:
为什么 Java 中只有值传递?
例子
* 一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 };
System.out.println(arr[0]);
change(arr);
System.out.println(arr[0]);
}
public static void change(int[] array) {
// 将数组的第一个元素变为0
array[0] = 0;
}
结果:
1
0
复制代码
分析: array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的是同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。
很多程序设计语言(特别是,C++和 Pascal)提供了两种参数传递的方式:
值调用和引用调用。
有些程序员(甚至本书的作者)认为 Java 程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。
例子
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s1 = new Student("小张");
Student s2 = new Student("小李");
Test.swap(s1, s2);
System.out.println("s1:" + s1.getName());
System.out.println("s2:" + s2.getName());
}
public static void swap(Student x, Student y) {
Student temp = x;
x = y;
y = temp;
System.out.println("x:" + x.getName());
System.out.println("y:" + y.getName());
}
复制代码
}
结果:
x:小李
y:小张
s1:小张
s2:小李
解释: 方法并没有改变存储在变量 s1 和 s2 中的对象引用。 swap 方法的参数 x 和 y 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝。
总结
简述线程、程序、进程的基本概念。以及他们之间关系是什么?
线程的基本状态
Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态(图源《Java 并发编程艺术》4.1.4 节)。
线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。
由上图可以看出:
线程创建之后它将处于 NEW(新建) 状态,调用 start() 方法后开始运行,线程这时候处于 READY(可运行) 状态。可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 RUNNING(运行) 状态。
当线程执行 wait()方法之后,线程进入 WAITING(等待)状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 TIME_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制,比如通过 sleep(long millis)方法或 wait(long millis)方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。线程在执行 Runnable 的run()方法之后将会进入到 TERMINATED(终止) 状态。
注释:
操作系统隐藏 Java 虚拟机(JVM)中的 READY 和 RUNNING 状态,它只能看到 RUNNABLE 状态,所以 Java 系统一般将这两个状态统称为 RUNNABLE(运行中) 状态 。
关于final关键字的一些总结
final 关键字主要用在三个地方:变量、方法、类。
Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java 虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 异常由 Java 虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以 0 时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。
注意:异常和错误的区别:异常能被程序本身处理,错误是无法处理。
Throwable 类常用方法:
异常处理总结:
但在以下 4 种特殊情况下,finally 块不会被执行:
当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖原始的返回值。如下:
public static int f(int value) {
try {
return value * value;
} finally {
if (value == 2) {
return 0;
}
}
复制代码
}
如果调用 f(2),返回值将是 0,因为 finally 语句的返回值覆盖了 try 语句块的返回值。
Java 序列化中如果有些字段不想进行序列化,怎么办?
对于不想进行序列化的变量,使用 transient 关键字修饰。
transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法。
补充:
获取键盘输入的两种方法
方法 1:通过 Scanner
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();
复制代码
方法 2:通过 BufferedReader
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
复制代码
按操作方式分类结构图:
按操作对象分类结构图:
既然有了字节流,为什么还要有字符流?
问题本质想问:不管是文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么 I/O 流操作要分为字节流操作和字符流操作呢?
回答:字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
BIO,NIO,AIO 有什么区别?
常见关键字总结:static,final,this,super
final 关键字(final关键字主要用在三个地方:变量、方法、类)
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
当用final修饰一个类时,表明这个类不能被继承。final类中的所有成员方法都会被隐式地指定为final方法。
使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的Java版本已经不需要使用final方法进行这些优化了)。类中所有的private方法都隐式地指定为final。
static 关键字
static 关键字主要有以下四种使用场景:
修饰成员变量和成员方法: 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。调用格式:类名.静态变量名 类名.静态方法名()
静态代码块: 静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次.
静态内部类(static修饰类的话只能修饰内部类): 静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:
静态导包(用来导入类中的静态资源,1.5之后的新特性): 格式为:import static 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。
this 关键字
this关键字用于引用类的当前实例。 例如:
class Manager {
Employees[] employees;
void manageEmployees() {
int totalEmp = this.employees.length;
System.out.println("Total employees: " + totalEmp);
this.report();
}
void report() { }
}
复制代码
在上面的示例中,this关键字用于两个地方:
此关键字是可选的,这意味着如果上面的示例在不使用此关键字的情况下表现相同。 但是,使用此关键字可能会使代码更易读或易懂。
super 关键字
super关键字用于从子类访问父类的变量和方法。 例如:
public class Super {
protected int number;
protected showNumber() {
System.out.println("number = " + number);
}
}
public class Sub extends Super {
void bar() {
super.number = 10;
super.showNumber();
}
}
复制代码
在上面的例子中,Sub 类访问父类成员变量 number 并调用其其父类 Super 的 showNumber() 方法。
使用 this 和 super 要注意的问题:
简单解释一下:
被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享。而 this 代表对本类对象的引用,指向本类对象;而 super 代表对父类对象的引用,指向父类对象;所以, this和super是属于对象范畴的东西,而静态方法是属于类范畴的东西。
该文章转自Guide哥GitHub上的的javaGuide项目,是楼主为了学习进行的整理
原文:https://www.cnblogs.com/aishangJava/p/13936265.html