赞
踩
class Mammal {}
class Cat extends Mammal { }
List<Mammal> list = new ArrayList<Cat>();
上述代码将出现编译错误。这是因为为List
指定了Mammal
哺乳动物为其元素类型,而为ArrayList
指定了Cat
为其元素类型 。在涉及到集合类型时,Java规则是,变量声明的类型必须与其实现的类型相匹配。因此,需要在List
和 ArrayList
中指定相同的数据类型。
class Mammal {}
class Cat extends Mammal { }
List<? super Mammal> mList = new ArrayList<Cat>(); // line 1
第1行将导致编译错误。这是因为Cat
的层次结构比其超类Mammal
要低。因此,只有当<Cat>
替换为<Mammal>
或<Object>
时,上述代码才会编译通过。
List<?> mylist = new ArrayList<? extends Mammal>();
上面代码创建了一个名为mylist
的列表, 并在列表的类型声明中使用了通配符 (?
),会导致编译错误,因为不能够在声明部分使用范型通配符。泛型的通配符仅允许在出现在方法参数或返回值中。
public void getList(T t)
上述代码使用范型参数声明了名为getList()
的方法(范型方法)但却没有定义范型参数,因而是错误的。 下述两种方式均可解决此问题:
//方式一,定义范型类
public class MyClass<T> {
public void getList(T t){
}
}
//方式二,定义范型方法
public class MyClass {
public <T> void getList(T t){
}
}
有界泛型类型有助于限制可用作泛型参数的类型。如下代码示例:
public class Shape {
}
public class Circle extends Shape{
}
public class ShapeDrawer<T extends Shape> {
public void drawShape(T shape) {
System.out.println(“Drawing Shape..”);
}
}
上述代码声明了一个名为Shape
的超类,Circle
类是Shape
的子类。代码还声明了一个ShapeDrawer
类, 这是一个范型类 ,它并没有直接定义范型类型T
,而是指定T 必须扩展Shape
类, 以对T
的类型实施了进一步限定, 这意味着范型 T
应该是Shape
的子类,如果用不是Shape
子类的参数调用drawShape()
方法 ,将发生编译错误。
java.util.Collection
接口是Java 集合 API中的顶级接口。 它仅仅简单表达了作为一个整体单元操作的一组值。 它有两个主要的子接口Set
和List
。 两者都用于存储一组数据值; 但存储方式略有不同。
List
允许重复的元素,同一元素可以多次出现。 此外,List
是有序的,列表中的元素按插入的顺序存储,并保持此顺序。
Set
不允许元素重复,而且其中的元素是无序的,Set
不会保留 元素插入顺序。
Map
接口也是一个顶级接口,用于存储键值对。
作为集合框架的一部分,Java提供java.util.Arrays
类 , 该类有几种实用方法, 可用于搜索数组中的特定元素。其中一种方法是Arrays.binarySearch
方法(二分搜索法), 它返回一个整数, 表示正在搜索的元素(如果存在)的在数组中的索引, 如果不存在,返回 -1
。 以下代码演示了其使用方法:
String [] strArr = {“one”, “two”, “four”};
System.out.println(“Search index of one is:
”+Arrays.binarySearch(strArr, “one”));
//输出
Search index of one is:0
如果想避免重复并且不关心顺序,则应使用HashSet
。HashSet
是Set
接口的实现,它是未排序和无序的。TreeSet
和LinkedHashSet
也是Set
的实现。二者都不允许重复。但TreeSet
保持排序顺序,而LinkedHashSet
保持插入顺序。
Queue
接口有有一个名为poll()
的方法,可用于删除队列的头部。以下代码演示了这一点:
Queue<Integer> lList = new LinkedList<Integer>();
lList.add(100);
lList.add(200);
System.out.println(“Removed– “:lList.poll()); // Line 1
此代码创建一个队列,并向其添加两个整数值。然后,它调用poll()
方法。 输出如下:
The Element removed is: 100
因此,poll()
方法总是删除队列的头部,即在队列中添加的第一个元素。
ArrayList
是List
接口的实现。它允许增加或缩减其尺寸。它有增加/缩减方法,可以增加或减少列表的大小。它还提供了对其元素的索引访问。 如下代码简单演示了ArrayList
:
List<Integer> myList = new ArrayList<Integer>();
myList.add(2);
myList.add(4);
myList.add(6);
myList.remove(1);
Integer num = myList.get(0);
Queue
接口。队列接口是Java 集合 API的一部分,它扩展了java.util.Collection
接口。除了正常的集合操作外,队列还提供了FIFO(先进先出)等队列数据结构的功能。队列接口有 下面几种操作队列的方法:
Comparator
接口 。Comparator
是一个可用于排序的接口。自Java 8以来,它一直被指定为函数接口。它仅有单个名称为compare
的抽象方法。该方法接受两个对象,并返回一个布尔值,表示比较结果。比较器接口也可用于对自定义对象进行排序,为此,相应的类需要实现比较器接口,并为其compare
方法提供具体实现。如果是Java 8或更高版本, 还可以使用lambda表达式实现比较器。
下表是队列主要有下面一些方法:
方法 | 描述 |
---|---|
offer(E e) | 将对象添加到队列中。如果失败 ,不会抛出异常。 |
peek() | 返回队列中最后添加的对象。 |
poll() | 检查并返回队列中头元素,如果存在,将其从队列中移除。 |
remove(Object o ) | 从队列中移除对象。 |
add(E e) | 将对象添加到队列中。如果添加失败,则抛出异常。 |
可以混合范型集合和非范型集合。如下代码片段示例:
public void insertDouble() {
List<Double> myDoubleList = new ArrayList<Double>();
myDoubleList.add(2.0); // Line 1
myDoubleList.add(4.0); // Line 2
insertSomethingElse(myDoubleList);
}
public void insertSomethingElse(List myDoubleList) {//Line 3
myDoubleList.add(“Hello World”); //Line 4
}
在上述代码中,第1行和第2行添加两个双精度Double
数到列表中,该列表是一个类型为Double
的范型列表。 在第3行的insertSomethingElse()
方法声明中,没有为参数myDoubleList
变量指定泛型类型。因此,任何类型的值都可以添加到此列表中。第4行将一个字符串添加到该列表中。
以下是Collection 接口上的一些重要方法:
方法 | 描述 |
---|---|
add | 向集合中添加对象。 |
remove | 从集合中移除对象。 |
contains | 检查集合中是否存在对象。 |
size | 返回集合的长度。 |
iterate | 在集合上进行迭代。 |
包装类是对应于Java基础类型的类,其主要作用是将基础类型转换为对象类型,反之亦然。使用Collections
时,包装类特别有用。由于Collections
不接受原始类型,应使用相应的包装类型进行包装。Java支持以下包装类:
基础类型 | 包装类型 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
public class BooleanDemo {
public static void main(String a[]){
//create Boolean using boolean primitive type
boolean boo1 = true;
Boolean booObj1 = new Boolean (boo1); //line 1
System.out.println(“Wrapper class Boolean output: “+booObj1);
Boolean booObj2 = new Boolean (“false”); //line 2
System.out.println(“Wrapper class Boolean output: “+booObj2);
System.out.println(booObj1.booleanValue());
}
}
第1行创建一个布尔包装器类型boolObj1
,对应于bool
类型变量。第2行使用String
值创建一个布尔包装器boolObj2
。Java自动将包装类型转换为基础类型。此外,在布尔包装类上有一个名为booleanValue()
的方法,返回原始布尔值。因此,上述代码将打印以下输出:
Wrapper class Boolean output: true
Wrapper class Boolean output: false
true
“100.55”
转换为双精度(Double
)?如何将双精度数100.55
转换为字符串?可以使用 Double.parseDouble()
方法将字符串转换为Double
,如下所示:
Double doubleValue = Double.parseDouble(“100.55”);
可以使用Double.toString()
方法将Double
转换为字符串,如下所示:
String stringValue = Double.toString(100.55);
Double doubleValue = Double.parseDouble(“Java”);
System.out.println(doubleValue);
上述代码编译没有问题,但是在执行时会抛出NumberFormatException
,这是因为JVM无法将字符串Java
转换为Double
值。
Java是一种面向对象的语言。但是Java中的基础数据类型不是对象类型。有时,需要这些类型的对象等价物。比如使用Collections
时, 由于集合只能包含对象,不能包含基础类型。为了解决这个问题,Java引入了包装类,用来将基础类型包装成对象。每个基础类型都有一个相应的包装类,可以创建该类型的对象。同样,也可以将包装对象转换为基础类型。例如,与int
基础类型对应的包装器类是 java.lang.Integer
。Java在基础类型和相应的包装类之间自动转换。
自动装箱(和自动拆箱)是Java 5中引入的一项功能,它可以帮助程序员减少一些冗余代码。
例如,为了给包装类分配一个基础类型的值,如果没有自动装箱,则需要按以下方式编写包装代码:
Integer integerValue = new Integer(1000); //wrapping
通过自动装箱,上述代码可以重写如下:
Integer integerValue = 1000;
同样,为了将包装类型的值分配给基础类型 ,需要编写解包代码。如下示例:
Integer integerValue = 1000;
int iValue = integerValue.intValue(); //unwrapping
通过自动装箱,上述代码可以重写如下:
Integer integerValue = 1000;
int iValue = integerValue;
在Java中创建线程有两种方法:
通过扩展Thread
类:在这种方法中, 需要创建一个扩展内置Thread
类的类,重写其run()
方法。如下代码示例:
public class MyThread extends Thread {
public void run(){
System.out.println(“In Thread Body..”);
}
}
通过实现Runnable
的接口:在这种方法中,需要创建一个实现内置Runnable
接口的类, 实现该接口的run()
方法。 如下代码所示 :
public class MyThread implements Runnable{
public void run(){
System.out.println(“In Thread Body..”);
}
}
Java中有2种类型的线程: 用户定义的线程和守护线程。
用户定义的线程是由用户以编程方式创建的线程, 它们的优先级较高。JVM会等待这些线程执行完成。
守护线程主要由JVM创建(虽然用户定义的线程也可以显式设置为守护线程),通常用于后台进程,如垃圾收集。
一旦所有用户线程(非Daemon线程)停止运行,JVM就会停止运行,JVM并不会等待守护线程停止,或执行完成。
start()
方法,直接调用其run()
方法?可以直接调用线程对象的run()
方法,而不调用其start()
方法。但这没有什么意义,因为,它不会产生新的线程,run()
方法中的代码将在调用它的同一线程中执行。
class Test {
public static void main(String argument[]) {
System.out.println(“Thread Example...”);
}
}
执行上述代码时,JVM会首先创建一个线程, 该线程执行main
方法中的代码。 执行完毕,线程终止。
Thread
类主要有哪些方法?方法 | 描述 |
---|---|
start() | 启动线程并调用 run() 方法。 |
run() | 定义线程中执行的代码。 |
sleep() | 使运行中的线程暂停指定的时间。 |
setName() | 将线程的名称设置为指定的值。 |
join() | 在不超过指定的多时间内,等待线程死亡。 |
isAlive() | 返回一个布尔值,指示当前线程是否存活。 |
setPriority() | 将线程的优先级更改为指定的值。 |
线程有下面五个状态。如下表所示:
状态 | 描述 |
---|---|
New (新建) | 创建了一个新的线程,但尚未调用 start() 方法时,线程处于新状态。 |
Runnable (可运行) | 在调用 start() 方法后,但线程调度程序尚未开始执行线程之前,线程处于可运行状态。 |
Running (正在运行) | 当线程调度程序开始执行线程时,线程处于运行状态,执行 run() 方法体内的代码。 |
Waiting / Blocking(等待/阻塞) | 如果线程是活动的,但不符合运行条件,则处于阻塞状态。 |
Dead (死亡) | 线程执行完毕后,进入死亡状态。 |
Thread
类创建线程 ?以下代码使用“线程”类创建一个线程:
class MyThread extends Thread {
public static void main(String argument[]) {
MyThread thread = new MyThread ();
thread.start(); // Line 1
}
public void run() {
System.out.println(“Inside Run Method..”);// Line 2
}
}
上面的代码定义了一个名为MyThread
的类,该类扩展了Thread
类。执行第1行时,会生成一个新线程,然后该线程将执行run()
方法中的代码。
Runnable
接口创建线程?下面代码通过实现Runnable
接口创建一个新线程:
class MyThread implements Runnable {
public static void main(String argument[]) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread); //Line 1
thread.start(); // Line 2
}
public void run() {
System.out.println(“Inside Run Method..”);
}
}
上面的代码定义了一个名为MyThread
的类,该类实现了Runnable
接口 。然后, 创建了一个该类的对象 myThread
,并在第1行将其传递给Thread
类以创建一个新线程对象。 第2行调用start()
方法生成一个新线程, 并执行run()
方法中定义的代码。
可以在Java程序中创建多个线程。对于两个线程相互通信,需要使用来自Object
类的方法wait()
、notify()
和notifyAll()
。
wait()
方法导致当前线程暂停,直到其他线程在同一对象上调用notify()
。
如果有许多对象都在等待特定对象,notify()
方法会导致任何一个线程恢复(执行)。notifyAll()
方法会使所有等待特定对象的线程恢复(执行)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。