java 知识点
- java 访问控制修饰符:private default protected public(pdpp)
修饰符 | 同类 | 同包 | 子类 | 所有类 |
---|---|---|---|---|
private | 允许访问 | |||
default | 允许访问 | 允许访问 | ||
protected | 允许访问 | 允许访问 | 允许访问 | |
public | 允许访问 | 允许访问 | 允许访问 | 允许访问 |
-
Java使用修饰符来修饰类中方法和属性。主要有两类修饰符:
访问控制修饰符 : default, public , protected, private
非访问控制修饰符 : final, abstract, strictfp -
static 修饰符,用来创建类方法(静态方法)和类变量(静态变量),只能通过类来访问。
static 方法、static 变量,只能通过类来访问。
static 修饰的 | 类方法, | 类变量, | 内部类, |
---|---|---|---|
只能通过类来访问。 | 只能通过类来访问。 | 可直接作为一个普通类使用。 |
- final 修饰符,用来修饰类、方法和变量。
final 修饰的类不能被继承;
final 修饰的方法不能被继承类重新定义;
final 修饰的变量为常量,不可修改。
final 修饰的 | 类, | 方法, | 变量, |
---|---|---|---|
不能被继承。 | 不能被继承类重新定义。 | 为常量,不可修改。 |
-
abstract 修饰符,用来创建抽象类和抽象方法。
-
java 的基本数据类型(共8种):
四种整型 (byte, short, int, long),两种浮点型(float、double),一种字符型(char),一种布尔型
String不是基本数据类型。
Java共有六个包装类,分别是Boolean、Character、Integer、Long、Float和Double,分别对应于 boolean、char、int、long、float和double。
byte 和 short 没有包装类。
byte:8位,最大存储数据量是255,存放的数据范围是-128~127之间。
short:16位,最大数据存储量是65536,数据范围是-32768~32767之间。
int:32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1。
long:64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1。
float:32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F。
double:64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加。
boolean:只有true和false两个取值。
char:16位,存储Unicode码,用单引号赋值。
-
问题:
在一个package中,a.java中引用b.java,在该package目录下编译a.java时,会提示找不到b.java。
解决:
在package的上级部门执行:
javac package/a.java -
问:
short a = 1;
short b = 2;
那么 a+b 是什么类型?
答:在java里,如果比int类型小的类型做运算,java在编译的时候就会将它们统一强转成int类型。
当是比int类型大的类型做运算,就会自动转换成它们中最大类型那个。
-
在java中,字节是占1个Byte,即8位;而字符是占2个Byte,即16位。
而且,需要注意的是,java的字节是有符号类型,而字符是无符号类型! -
多线程中,wait会释放锁,sleep不会释放锁,yield不会释放锁。
不会释放锁:也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。
wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。
sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”。
yield()的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行! -
多线程中,park和wait的区别:
wait让线程阻塞前,必须先通过synchronized获取同步锁。 -
Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。
不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的;而Condition是需要与"互斥锁"/"共享锁"捆绑使用的。 -
Java语言使用内存的时候,栈主要保存 基本数据类型和对象的引用,而堆存储对象。
java内存,分为栈内存、堆内存。 -
问:什么时候使用泛型?
答:泛型不是必需的,但是java强烈推荐使用集合框架时使用泛型,他可以有效地避免数据类型不同时引发的各种问题,提高处理速度,方便使用。
举例:
public <T extends BaseModelJso> JsArray<T> convertToJsArray(List<T> arraylist) {
JsArray<T> ja = JsArray.createArray().cast();
for(T jo : arraylist){
ja.push(jo);
}
return ja;
}
泛型例子:使用泛型方法打印不同字符串的元素
public class GenericMethodTest
{
// 泛型方法 printArray
public static < E > void printArray( E[] inputArray )
{
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
public static void main( String args[] )
{
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "整型数组元素为:" );
printArray( intArray ); // 传递一个整型数组
System.out.println( "\n双精度型数组元素为:" );
printArray( doubleArray ); // 传递一个双精度型数组
System.out.println( "\n字符型数组元素为:" );
printArray( charArray ); // 传递一个字符型数组
}
}
-
Java 文档注释
Java 有三种注释方式。分别是 // 和 /* /,第三种被称作说明注释,它以 /* 开始,以 */结束。
说明注释允许你在程序中嵌入关于程序的信息。 -
独立的 Java 应用程序和 applet 程序之间重要的不同:
Java 中 Applet 类继承了 java.applet.Applet 类。
Applet 类没有定义 main(),所以一个 Applet 程序不会调用 main() 方法。
Applet 被设计为嵌入在一个 HTML 页面。
当用户浏览包含 Applet 的 HTML 页面,Applet 的代码就被下载到用户的机器上。
要查看一个 Applet 需要 JVM。 JVM 可以是 Web 浏览器的一个插件,或一个独立的运行时环境。
用户机器上的 JVM 创建一个 Applet 类的实例,并调用 Applet 生命周期过程中的各种方法。
Applet 有 Web 浏览器强制执行的严格的安全规则,Applet 的安全机制被称为沙箱安全。
Applet 需要的其他类可以用 Java 归档(JAR)文件的形式下载下来。 -
创建一个线程
Java 提供了三种创建线程的方法:
通过实现 Runnable 接口;
通过继承 Thread 类;
通过 Callable 和 Future 创建线程。 -
创建一个线程,最简单的方法:是创建一个实现 Runnable 接口的类。
为了实现 Runnable,一个类只需要执行一个方法调用 run(),声明如下:
public void run()
你可以重写该方法,重要的是理解 run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。
在创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象。 -
创建一个线程的第二种方法:创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。
继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。
该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。 -
创建一个线程的第三种方法: 通过 Callable 和 Future 创建线程
- 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
- 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
- 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
- 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
- 创建线程的三种方式的对比
- 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
- 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
-
Java 多线程编程
Java 给多线程编程提供了内置的支持。一个多线程程序包含两个或多个能并发运行的部分。程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。 -
一个线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
下图显示了一个线程完整的生命周期。
-
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。 -
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。 -
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。 -
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
- 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
-
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。 -
线程的优先级
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。
但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。 -
进程概念:
一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守候线程都结束运行后才能结束。 -
Socket 编程
套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。
当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行进行通信。
java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。 -
以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:
服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。
服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。
Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。
在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。
连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。
TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送 -
java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。
-
客户端当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。
-
Java 泛型
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 -
Java 序列化
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。
类 ObjectInputStream 和 ObjectOutputStream 是高层次的数据流,它们包含序列化和反序列化对象的方法。 -
一个类的对象要想序列化成功,必须满足两个条件:
- 该类必须实现 java.io.Serializable 对象。
- 该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。
-
检验一个类的实例是否能序列化十分简单, 只需要查看该类有没有实现 java.io.Serializable接口。
-
当对象被序列化时,属性 SSN 的值为 111222333,但是因为该属性是短暂的,该值没有被发送到输出流。所以反序列化后 Employee 对象的 SSN 属性为 0。
-
包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。
如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。
在 java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条。
如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。 -
可以使用下面3种方法之一来引用其他包中的类。
- 使用类全名描述,例如:
payroll.Employee - 用 import 关键字引入,使用通配符 ""
import payroll.; - 使用 import 关键字引入 Employee 类:
import payroll.Employee;
-
java集合框架位于java.util包中, 所以当使用集合框架的时候需要进行导包。
-
Collection 接口
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。
List 接口
List接口是一个有序的Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的小标)来访问List中的元素,而且允许有相同的元素。
Set
Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。 -
Set和List的区别
- Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
- Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
- List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector> 。
-
Java中的数据结构主要包括以下几种接口和类:
枚举(Enumeration)
位集合(BitSet)
向量(Vector)
栈(Stack)
字典(Dictionary)
哈希表(Hashtable)
属性(Properties)
集合框架 (Collection)(在Java2中引入) -
枚举(The Enumeration)接口定义了一种从数据结构中取回连续元素的方式。
枚举定义了一个叫 nextElement 的方法,该方法用来得到一个包含多元素的数据结构的下一个元素。 -
向量(Vector)
向量(Vector)类和传统数组非常相似,但是Vector的大小能根据需要动态的变化。
和数组一样,Vector对象的元素也能通过索引访问。
使用Vector类最主要的好处就是在创建对象的时候不必给对象指定大小,它的大小会根据需要动态的变化。 -
字典(Dictionary)
字典(Dictionary) 类是一个抽象类,它定义了键映射到值的数据结构。
当你想要通过特定的键而不是整数索引来访问数据的时候,这时候应该使用Dictionary。
由于Dictionary类是抽象类,所以它只提供了键映射到值的数据结构,而没有提供特定的实现。 -
哈希表(Hashtable)
Hashtable类提供了一种在用户定义键结构的基础上来组织数据的手段。
例如,在地址列表的哈希表中,你可以根据邮政编码作为键来存储和排序数据,而不是通过人名。
哈希表键的具体含义完全取决于哈希表的使用情景和它包含的数据。 -
属性(Properties)
Properties 继承于 Hashtable.Properties 类表示了一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。
Properties 类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值。 -
接口的多重继承
在Java中,类的多重继承是不合法,但接口允许多重继承。 -
接口有以下特性:
接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键子。
接口中的方法都是公有的。 -
抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
-
接口:
接口没有构造方法。
接口中所有的方法必须是抽象方法。
接口不能包含成员变量,除了 static 和 final 变量。
接口支持多重继承。 -
接口特性
接口中每一个方法都是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。 -
类描述对象的属性和方法,接口则包含类要实现的方法。
-
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。 -
Java 抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。 -
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
构造方法,类方法(用static修饰的方法)不能声明为抽象方法。 -
多态的实现方式
方式一:重写
方式二:接口
方式三:抽象类和抽象方法 -
重写形式的多态,存在的三个必要条件:
继承
重写
父类引用指向子类对象 -
多态的形象比喻:1.重写:都是打印动作,彩色打印机打印彩色,黑白打印机打印黑白。2.重载:彩色打印机用不同的颜色打印相应颜色。
-
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作 -
重写(Override)
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。
在面向对象原则里,重写意味着可以重写任何现有方法。 -
编译阶段,只是检查参数的引用类型;运行时,Java虚拟机(JVM)指定对象的类型并且运行该对象的方法
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
尽管b属于Animal类型,但是它运行的是Dog类的move方法。
这是由于在编译阶段,只是检查参数的引用类型。
然而在运行时,Java虚拟机(JVM)指定对象的类型并且运行该对象的方法。
因此在上面的例子中,之所以能编译成功,是因为Animal类中存在move方法,然而运行时,运行的是特定对象的方法。
-
方法的重写规则:
声明为final的方法不能被重写。
声明为static的方法不能被重写,但是能够被再次声明。 -
静态的方法可以被继承,但是不能重写。如果父类中有一个静态的方法,子类也有一个与其方法名,参数类型,参数个数都一样的方法,并且也有static关键字修饰,那么该子类的方法会把原来继承过来的父类的方法隐藏,而不是重写。通俗的讲就是父类的方法和子类的方法是两个没有关系的方法,具体调用哪一个方法是看是哪个对象的引用;这种父子类方法也不在存在多态的性质。
-
Super关键字
当需要在子类中调用父类的被重写方法时,要使用super关键字。 -
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
-
声明为final的方法不能被重写。
声明为static的方法不能被重写,但是能够被再次声明。
构造方法不能被重写。 -
重载(Overload)
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
参数不同,是指参数个数或类型或顺序不同。 -
构造器
子类不能继承父类的构造器(构造方法或者构造函数),但是父类的构造器带有参数的,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。
如果父类有无参构造器,则在子类的构造器中用super调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。 -
final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写。
注:实例变量也可以被定义为 final,被定义为 final 的变量不能被修改。被声明为 final 的类的方法自动地声明为 final,但是实例变量并不是 final -
super 与 this 关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。 -
java.lang 包是默认加载到所有的 Java 程序的。
问:默认加载到所有的 Java 程序的包,有几个?
答:一个,java.lang 包。 -
如果一个方法没有捕获一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。 -
catch 不能独立于 try 存在。
在 try/catch 后面添加 finally 块并非强制性要求的。
try 代码后不能既没 catch 块也没 finally 块。
try, catch, finally 块之间不能添加任何代码。 -
声明自定义异常
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。 -
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例和方法。
继承的优点:维护性高,代码简洁,提高代码的复用性
继承的缺点:耦合性高 -
所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。
-
Java的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如A类继承B类,B类继承C类,所以按照关系就是C类是B类的父类,B类是A类的父类,这是java继承区别于C++继承的一个特性。
-
使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
-
位运算符&|^可以充当布尔逻辑运算符(前提是两边的数据类型为布尔类型)。
进行布尔判断时,位运算符与布尔逻辑运算符的区别是:
位运算符是:非短路运算
布尔逻辑运算符是:短路运算 -
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。 -
读取控制台输入
Java 的控制台输入由 System.in 完成。
为了获得一个绑定到控制台的字符流,你可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流。
下面是创建 BufferedReader 的基本语法:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedReader 对象创建后,我们便可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。
-
可变参数
JDK 1.5 开始,Java支持传递同类型的可变参数给一个方法。
方法的可变参数的声明如下所示:
typeName... parameterName
在方法声明中,在指定参数类型后加一个省略号(...) 。
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。 -
方法的参数范围涵盖整个方法。参数实际上是一个局部变量。
for循环的初始化部分声明的变量,其作用范围在整个循环。
循环体内声明的变量其适用范围是从它声明到循环体结束。
你可以在一个方法里,不同的非嵌套块中多次声明一个具有相同的名称局部变量,但你不能在嵌套块内两次声明局部变量。 -
如何才能设置和获取日期数据的特定部分呢,比如说小时,日,或者分钟? 我们又如何在日期的这些部分加上或者减去值呢? 答案是使用Calendar 类。
Calendar类的功能要比Date类强大很多,而且在实现方式上也比Date类要复杂一些。
Calendar类是一个抽象类,在实际使用时实现特定的子类的对象,创建对象的过程对程序员来说是透明的,只需要使用getInstance方法创建即可。 -
Calendar类实现了公历日历,GregorianCalendar是Calendar类的一个具体实现。
Calendar 的getInstance()方法返回一个默认用当前的语言环境和时区初始化的GregorianCalendar对象。 -
Java 休眠(sleep)
sleep()使当前线程进入停滞状态(阻塞当前线程),让出CPU的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会。
你可以让程序休眠一毫秒的时间或者到您的计算机的寿命长的任意段时间。
Thread.sleep(1000*3); // 休眠3秒
- 测量时间间隔(以毫秒为单位):
long start = System.currentTimeMillis( );
long end = System.currentTimeMillis( );
long diff = end - start;
-
Java使用以下三种方法来比较两个日期:
使用 getTime() 方法获取两个日期(自1970年1月1日经历的毫秒数值),然后比较这两个值。
使用方法 before(),after() 和 equals()。例如,一个月的12号比18号早,则 new Date(99, 2, 12).before(new Date (99, 2, 18)) 返回true。
使用 compareTo() 方法,它是由 Comparable 接口定义的,Date 类实现了这个接口。 -
foreach 循环
JDK 1.5 引进了一种新的循环类型,被称为 foreach 循环或者加强型循环,它能在不使用下标的情况下遍历数组。
double[] myList = {1.9, 2.9, 3.4, 3.5};
// 打印所有数组元素
for (double element: myList) {
System.out.println(element);
}
- 创建数组的2种方式:
dataType[] arrayRefVar = new dataType[arraySize];
dataType[] arrayRefVar = {value0, value1, ..., valuek};
-
创建字符串最简单的方式如下:
String greeting = "菜鸟教程";
在代码中遇到字符串常量时,这里的值是 "菜鸟教程"",编译器会使用该值创建一个 String 对象。 -
注意:String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了。
如果需要对字符串做很多修改,那么应该选择使用 StringBuffer & StringBuilder 类。 -
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
-
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。 -
基本类型 char 的包装类为:Character。
-
Math 类
Math 的方法都被定义为 static 形式,通过 Math 类可以在主函数中直接调用。 -
Number 类
所有的包装类(Byte、Short、Integer、Long、Float、Double)都是抽象类 Number 的子类。
Number 类属于 java.lang 包。
int 的包装类是 Integer,其他都是相应首字母大写。 -
switch 语句有如下规则:
switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串类型了,同时 case 标签必须为字符串常量或字面量。 -
问:switch语句能否作用在byte上,能否作用在long上,能否作用在String上?
答:switch语句能作用在 byte、short、int、char 及对应的包装类上,不能作用在 long 上。原因:switch 语句必须作用在 int 或 枚举类型(menu) (jdk 1.6 开始支持枚举类型)上,byte、short、char 会自动转换为 int 类型。
jdk 1.7 后,可以作用在 String 上(原理:用string的hash值(int型,hashCode()的返回值)代替string进行switch)。 -
continue 关键字
continue 适用于任何循环控制结构中。作用是让程序立刻跳转到下一次循环的迭代。
在 for 循环中,continue 语句使程序立即跳转到更新语句。
在 while 或者 do…while 循环中,程序立即跳转到布尔表达式的判断语句。 -
break 关键字
break 主要用在循环语句或者 switch 语句中,用来跳出整个语句块。
break 跳出最里层的循环,并且继续执行该循环下面的语句。 -
Java 增强 for 循环
Java5 引入了一种主要用于数组的增强型 for 循环。
Java 增强 for 循环语法格式如下:
for(声明语句 : 表达式)
{
//代码句子
}
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ){
System.out.print( x );
System.out.print(",");
}
打印结果:10,20,30,40,50,
- instanceof 运算符
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。
String name = "James";
boolean result = name instanceof String; // 由于 name 是 String 类型,所以返回真
在判断一个实例引用的类型时,使用的是实际类型,而不是声明的类型。
class Vehicle {}
public class Car extends Vehicle {
public static void main(String args[]){
Vehicle v2 = new Car(); // v2 是 Car 类型,而不是 Vehicle 类型。
Vehicle v3 = new Vehicle(); // v3 是 Vehicle 类型
//Car 是 Vehicle类型, Vehicle 不是 Car 类型
boolean result1 = v2 instanceof Car; // true
boolean result2 = v2 instanceof Vehicle; // true
boolean result3 = v3 instanceof Car; // false
boolean result4 = v2 instanceof Vehicle; // true
}
}
-
赋值运算符
=、+=、-= 等 -
位运算符:与( & )、或( | )、非( ~ )、异或( ^ )
Java定义了位运算符,应用于 byte、short、int、long、char 等类型。 -
逻辑运算符:与( && )、或( || )、非( !)
-
短路逻辑运算符
当使用与逻辑运算符时,在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false,这时候就不会再判断第二个操作了。 -
java 中双等号(==)和 equals()方法的区别,总结:
- 基本数据类型比较,应使用双等号(==)。
- 复合数据类型比较,可以使用双等号()和 equals()方法。
Object类中 equals() 方法初始行为是比较对象的内存地址,跟双等号()的结果相同;被复写后,按照复写的来。
String类的 equals() 方法被复写,复写成了比较两个字符串的内容。
-
java关系运算符 (双等号)
基本数据类型(也称原始数据类型: byte,short,int,long,float,double,char,boolean),他们之间的比较,应用双等号(),比较的是他们的值。
复合数据类型(类):当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址(确切的说,是堆内存地址)。
对于复合数据类型,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。因为每new一次,都会重新开辟堆内存空间。 -
equals()方法
- Object类中 equals() 方法
JAVA当中所有的类都是继承于Object这个超类的,在Object类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址,但在一些类库当中这个方法被复写了,如String、Integer、Date。在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
所以说,对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是内存中的存放位置的地址值,跟双等号(==)的结果相同;如果被复写,按照复写的来。 - String类的 equals() 方法
String类的 equals() 方法被复写,复写成了比较两个字符串的内容。
-
++num, --num : 先进行自增或者自减运算,再进行表达式运算
num++, num-- : 先进行表达式运算,再进行自增或者自减运算 -
java 文件中的 main 函数:
public static void main(String[] args)
不写 String[] args,会报错。
可以执行 class 文件时传递参数(args):java Test args1 args2 args3 -
JAVA 的类(Class)有 2 种访问权限: public、默认 (有一个特例,就是“内部类”,其可以是private或protected的)。
而方法和变量有 4 种:public、默认、protected、private。 -
java中类Class的访问权限:
public:可以供所有的类访问。
默认(什么也不写):默认可以称为friendly。但是,java语言中是没有friendly这个修饰符的,这样称呼应该是来源于c++。默认的访问权限是包级访问权限,等同于default。 -
synchronized 修饰符
synchronized 关键字声明的方法同一时间只能被一个线程访问。
synchronized 修饰符可以应用于四个访问修饰符。 -
volatile 修饰符,用于多线程同步
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
一个 volatile 对象引用可能是 null。 -
transient 修饰符
transient说明一个属性是临时的,不会被序列化。
序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
transient有“临时的”、"短暂的"含义。我们了解过Serializable(Java序列化),当对某些变量我们不想对它进行序列化的时候就可以将此变量设置为transient,transient是Java语言的关键字,用来表示一个属性不是该对象序列化的一部分。 -
abstract 修饰符
抽象类:
抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
抽象类可以不包含抽象方法。
抽象类可以包含抽象方法和非抽象方法。
抽象方法的声明以分号结尾,例如:public abstract sample();。 -
一个类不能同时被 abstract 和 final 修饰。
如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。 -
抽象方法
抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。
抽象方法不能被声明成 final 和 static。 -
final 类
final 类不能被继承,没有类能够继承 final 类的任何特性。 -
final 方法
类中的 final 方法可以被子类继承,但是不能被子类修改。
声明 final 方法的主要目的是防止该方法的内容被修改。 -
final 变量:
final 变量能被显式地初始化并且只能初始化一次。
被声明为 final 的对象的引用不能指向不同的对象。但是 final 对象里的数据可以被改变。也就是说 final 对象的引用不能改变,但是里面的值可以改变。
final 修饰符通常和 static 修饰符一起使用来创建类常量。 -
静态方法不能使用类的非静态变量。
-
局部变量不能被声明为 static 变量。
-
非访问修饰符
static 修饰符,用来创建类方法(静态方法)和类变量(静态变量)。
final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
abstract 修饰符,用来创建抽象类和抽象方法。
synchronized 和 volatile 修饰符,主要用于线程的编程。
transient -
方法继承的规则:
父类中声明为 public 的方法在子类中也必须为 public。
父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
父类中声明为 private 的方法,不能够被继承。 -
protected 访问修饰符不能修饰类和接口。方法和成员变量能够声明为 protected,但是接口的成员变量和成员方法不能声明为 protected。
-
Java 程序的 main() 方法必须设置成公有的(public),否则,Java 解释器将不能运行该类。
-
默认访问修饰符-不使用任何关键字,等同于 default
使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为 public static final,而接口里的方法默认情况下访问权限为 public。 -
类变量(静态变量)
类变量也称为静态变量,在类中以static关键字声明,但必须在方法构造方法和语句块之外。
无论一个类创建了多少个对象,类只拥有类变量的一份拷贝。
静态变量除了被声明为常量外很少使用。常量是指声明为public/private,final和static类型的变量。常量初始化后不可改变。
静态变量储存在静态存储区。经常被声明为常量,很少单独使用static声明变量。
静态变量在程序开始时创建,在程序结束时销毁。
与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为public类型。
默认值和实例变量相似。数值型变量默认值是0,布尔型默认值是false,引用类型默认值是null。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。
静态变量可以通过:ClassName.VariableName的方式访问。
类变量被声明为public static final类型时,类变量名称一般建议使用大写字母。如果静态变量不是public和final类型,其命名方式与实例变量以及局部变量的命名方式一致。 -
实例变量
实例变量声明在一个类中,但在方法、构造方法和语句块之外;
当一个对象被实例化之后,每个实例变量的值就跟着确定;
实例变量在对象创建的时候创建,在对象被销毁的时候销毁;
实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息;
实例变量可以声明在使用前或者使用后;
访问修饰符可以修饰实例变量;
实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有。通过使用访问修饰符可以使实例变量对子类可见;
实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定;
实例变量可以直接通过变量名访问。但在静态方法以及其他类中,就应该使用完全限定名:ObejectReference.VariableName。 -
Java 局部变量
局部变量声明在方法、构造方法或者语句块中;
局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁;
访问修饰符不能用于局部变量;
局部变量只在声明它的方法、构造方法或者语句块中可见;
局部变量是在栈上分配的。
局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。 -
Java语言支持的变量类型有:
类变量(静态变量):独立于方法之外的变量,用 static 修饰。
实例变量:独立于方法之外的变量,不过没有 static 修饰。
局部变量:类的方法中的变量。 -
强制类型转换
- 条件是转换的数据类型必须是兼容的。
- 格式:(type)value,其中:type是要强制类型转换后的数据类型
- 隐含强制类型转换
- 整数的默认类型是 int。
- 浮点型不存在这种情况,因为在定义 float 类型时必须在数字后面跟上 F 或者 f。
-
Java 里使用 long 类型的数据一定要在数值后面加上 L,否则将作为整型解析。
long value = 9223372036854775807L; -
自动类型转换
整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。
转换从低级到高级。
低 ------------------------------------> 高
byte,short,char—> int —> long—> float —> double -
数据类型转换必须满足如下规则:
- 不能对boolean类型进行类型转换。
- 不能把对象类型转换成不相关类的对象。
- 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
- 转换过程中可能导致溢出或损失精度,例如:
int i =128;
byte b = (byte)i;
因为byte类型时8位,最大值为127,所以当强制转换为int类型值128时候就会导致溢出。 - 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
(int)23.7 == 23;
(int)-45.89f == -45
-
常量是在程序运行时,不会被修改的量。
使用 final 关键字来修饰常量,声明方式和变量类似:final double PI = 3.1415927;
通常使用大写字母表示常量。
字面量可以赋给任何内置类型的变量。
byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。
当使用常量的时候,前缀0表示8进制,而前缀0x代表16进制。例如:
int decimal = 100;
int octal = 0144;
int hexa = 0x64; -
变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。
内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。
因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。 -
Java 有两大数据类型:
内置数据类型(byte,short,int,long,float,double,char,boolean)
引用数据类型
实际上,JAVA中还存在另外一种基本类型void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。 -
引用类型
在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。
对象、数组都是引用数据类型。
所有引用类型的默认值都是null。
一个引用变量可以用来引用任何与之兼容的类型。 -
一个类可以包含以下类型变量:
局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
类变量(静态变量):类变量也声明在类中,方法体之外,但必须声明为static类型。 -
构造方法
每个类都有构造方法。如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。 -
源文件声明规则
当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则。
一个源文件中只能有一个public类。
一个源文件可以有多个非public类。
源文件的名称应该和public类的类名保持一致。例如:源文件中public类的类名是Employee,那么源文件应该命名为Employee.java。
如果一个类定义在某个包中,那么package语句应该在源文件的首行。
如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。
import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。 -
java因强制要求类名(唯一的public类)和文件名统一,因此在引用其它类时无需显式声明。在编译时,编译器会根据类名去寻找同名文件。
-
""和equals方法究竟有什么区别?
比较基本数据类型或2个引用变量是否相等;
equals比较对象的内容;
操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同。要比较两个基本类型的数据或两个引用变量是否相等,只能用操作符。
如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如Objet obj = new Object();变量obj是一个内存,new Object()是另一个内存,此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就需要用操作符进行比较。
equals方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。例如,对于下面的代码:
String a=new String("foo");
String b=new String("foo");
两条new语句创建了两个对象,然后用a,b这两个变量分别指向了其中一个对象,这是两个不同的对象,它们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式ab将返回false,而这两个对象中的内容是相同的,所以,表达式a.equals(b)将返回true。 -
heap(堆) 和 stack(栈) 有什么区别?
方法局部变量栈(方法栈)。
java的内存分为两类,一类是栈内存,一类是堆内存。
栈内存是指程序进入一个方法时,为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这个栈中的变量也将随之释放。
堆是与栈作用不同的内存,一般用于存放不放在当前方法栈中的那些数据,例如,使用new创建的对象都放在堆里,所以,它不会随方法的结束而消失。方法中的局部变量使用final修饰后,放在堆中,而不是栈中。