win+R:敲“cmd”回车进行如下操作
1.切换目录:cd /d 目标目录
2.编译java源代码:javac -d . java源文件名称
3.运行.class文件(字节码文件):java 包.class类名
每次变量使用前必须要先申明,然后赋值,方可使用。eg:int i=20;
在计算机中数字的运算都是按照“补码”的形式运算的。 结果返回boolean值的表达式?“结果为true时内容”:“结果为false时内容”。 Java.util包中提供了一些集合类,常用的集合类有List集合、Map集合、Set集合继承关系如下: 常用方法如下: list集合实现了Collection接口并且定义了两个非常重要的方法: Array(数组)是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。 数组属于引用型变量,因此两个相同类型的数组如果具有相同的引用,它们就有完全相同的元素。 set集合中的对象不按特定的方式排序,且不能包含重复元素。 在 add 数据的时候,无论数据如何颠倒,下面输出顺序不变。 按自然顺序排序 。 map没有实现collection接口,提供的是Key到Value的映射,一个Key只能映射一个Value,map集合中允许存储null,且可以多个。 Array可以容纳基本类型和对象,而ArrayList只能容纳对象 常识:线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行 访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染;线程不安全就是不提供数据访问保护,多线程先后更改数据会产生数据不一致或者数据污染的情况。 线程安全必须要使用synchronized关键字来同步控制,所以会导致性能的降低。 HashMap内部是通过一个数组实现的,只是这个数组比较特殊,数组里存储的元素是一个Entry实体(jdk8为Node),这个Entry实体主要包含key、value以及一个指向自身的next指针。 HashMap和Hashtable都实现了Map接口,HashMap几乎可以等价于Hashtable,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization)以及速度。 HashMap的key和value都允许为null,而Hashtable的key和value都不允许为null。HashMap遇到key为null的时候,调用putForNullKey方法进行处理,而对value没有处理;Hashtable遇到null,直接返回NullPointerException。 HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。 HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。 由于Hashtable是synchronized即线程安全的,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。 Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。 ArrayList、HashMap、TreeMap和HashTable类提供对元素的随机访问。 抽象方法 使用 abstract 关键字修饰,仅有声明没有方法体的方法。 从某种意义上来说,抽象方法就是被用来重写的,所以在父类声明的抽象方法一定要在子类里面重写。如果真的不想在子类里面重写这个方法,那么可以再在子类里面把这个方法再定义为抽象方法,因为子类觉得我去实现也不合适,应该让继承我的子类去实现比较合适,因此也可以在继承这个子类的下一个子类里面重写在父类里面声明的抽象方法,这是可以的。 这里有一个规则:既然父类里面的方法是抽象的,那么对于整个类来说,它就有一个没有实现的方法,这个方法不知道怎么去实现,那么这个类是就是残缺不全的,因此这个类应该被定义为一个抽象类。所以前面这样声明的class Animal应该要在class的前面加上abstract,即声明成这样:abstract class Animal,这样Animal类就成了一个抽象类了。 抽象类的初衷是“抽象”,即规定这个类“是什么”,具体的实现暂不确定,是不完整的,因此不允许直接创建实例。 什么是接口(Interface) 其成员变量,默认由public static final修饰的(可省略),且必须显式初始化,所以均为常量,最好使用大写方式表示(名称过长使用“_”分隔); 接口(interface),在 java 中接口属于一种引用类型,是一个抽象方法的集合。它以 interface 来声明。一个类通过重写接口中的方法来实现一个接口。接口的主要作用是达到一种类似于行为统一的“协议”。接口内的方法、返回类型等它们默认都是 static 和 final 的。它只是提供了一种形式,并没有提供具体的实现。实现一个接口必须实现它的所有抽象方法。 Java 为了保证数据安全性是不能多继承的,也就是一个类只有一个父类。但是接口不同,一个类可以同时实现多个接口(多实现),不管这些接口之间有没有关系,所以接口弥补了抽象类不能多继承的缺陷。 接口是抽象类的延伸,它可以定义没有方法体的方法,要求实现者去实现。 接口是一种规范,接口定义了多个对象的共同行为,实现接口的对象可以分别实现自己的方法体。 接口使用instanceof关键字, (1)接口反映了对象以及对对象操作的本质。 在java8中。 应用场合很简单了 新建(new)、就绪(Runnable)、阻塞(Bolocked)、死亡(Dead)。 生活举例 Thread 本质上也是实现了 Runnable 接口的一个实例,它代表了一个线程的实例,并且启动线程的唯一方法,就是通过 Thread 类的 start() 的方法,start() 方法是一个 native (本地)方法,它将启动一个新线程,并执行 run 方法,(Thread 中提供的 run() 方法是一个方法),这种方式通过自定义直接 extend Thread,并重写 run() 方法,就可以启动新线程并执行自己定义的额 run() 方法,需要注意的是,调用 start() 方法后并不是立即执行多线程代码,而是使得该线程变为可运行状态(Runnable),什么时候运行多线程代码是由操作系统决定的 其实,不管是通过继承 Thread 类还是通过使用 Runnable 接口来实现多线成的方法,最终还是通过 Thread 的对象的 API 来控制线程的。 Callable 接口实际是属于 Executor 框架中的功能类,Callable 接口与 Runnable 接口的功能类似,但提供了比 Runnable 更强大的功能,主要表现为以下 3 点: Callable 可以在任务结束后提供一个返回值,Runnable 无法提供这个功能 Callable 中的 call 方法可以抛出异常,而 Runnable 的 run 方法不能抛出异常 运行 Callable 可以拿到一个 Future 对象,Future 对象表示异步计算的结果,它提供了检查计算是否完成的方法,由于线程属于异步计算模型,因此无法从别的线程中得到函数的返回值,在这种情况下,就可以使用 Future 来监视目标线程调用 call() 方法的情况,当调用 Future 的 get() 方法以获取结果时,当前线程就会阻塞以,直到 call() 方法结束返回结果 其次,很多 Java 开发人员认为,一个类仅在需要被加强或修改时,才会被继承,因此如果没有必要重写 Thread 类中的其它方法,那么继承 Thread 的实现方式与实现 Runnable 接口的效果相同,在这种情况下最好通过实现 Runnable 接口的方式来创建线程 引申:一个类是否可以同时继承 Thread 与实现 Runnable 接口? 首先说明是可以的,为了说明这个问题,给出如下示例: 通常,系统通过调用线程类的 start() 方法启动一个线程,此时该线程处于就绪状态,而非运行状态,也就意味着这个线程可以被 JVM 来调度执行,在调度过程中,JVM 通过调用线程类的 run() 方法来完成实际的操作,当 run() 方法结束后,线程就会终止 如果直接调用线程类的 run() 方法,这会被当作一个普通的函数,程序中仍然只有主线程这一个线程,也就是说 start() 方法能够异步的调用 run() 方法,但是直接调用 run() 方法却是同步的,因此也就无法达到多线程的目的,由此可见,只有通过调用线程类的 start() 方法才能达到多线程的目的 在 Java 语言中,每个对象都有一个对象锁与之相关联,该锁表明对象在任何时候只容许被一个对象所拥有,当一个线程调用对象的一段 synchronized 代码时,需要先获取这个锁,然后去执行相应的代码,执行结束后释放锁 当使用某个 synchronized 来修饰某个共享资源时,如果线程 A1 在执行 synchronized 代码,另外一个线程 A2 也将要同时执行同一对象的同一个 synchronized 代码时,线程 A2 将要等到线程 A1 执行完成后,才能继续执行,在这种情况下可以使用 notify() 方法和 wait() 方法 lock(): 以阻塞方式获取锁,也就是说如果获取到了锁,立即返回,如果别的线程持有锁,当前线程等待,直到获取锁后返回。 tryLock(): 以非阻塞方式获取锁,只是尝试性的去获取一下锁,如果获取到锁,立即返回 true,否则返回 false。 sleep() 方法与 wait() 方法都是使线程暂停执行一段时间的方法,具体而言,sleep() 方法与 wait() 方法的区别主要表现在如下几个方面: 由于 sleep() 方法的主要作用是让线程暂停执行一段时间,时间一到则自动恢复,不涉及线程间的通信,因此调用 sleep() 方法并不会释放锁,而 wait() 方法则不同,当调用 wait() 方法后,线程会释放掉它所占用的锁,从而使线程所在对象中的其他synchronized 数据可被别的线程使用 由于 wait() 方法的特殊意义,因此它必须放在同步控制方法或者同步语句块中使用,而 sleep() 方法可以在任何地方使用 在 Java 语言中,可以使用 stop() 方法与 suspend() 方法来终止线程的执行,当用 Thread.stop() 来终止线程时,它会释放已经锁定的所有监视资源,调用 suspend() 方法容易发生死锁(死锁指的是两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,如果无外力作用,它们都将无法推进),由于调用 suspend() 方法不会释放锁,这就会导致一个问题:如果用一个 suspend 挂起一个有锁的线程,那么在锁恢复之前将不会被释放,如果调用 suspend() 方法,线程将试图取得相同的锁,程序就会发生死锁 Java 提供了两种线程:守护线程与用户线程,守护线程又被称为“服务进程”或“后台线程”是指在程序运行时,在后台提供一种通用服务的线程,这种线程并不属于程序中不可或缺的部分,通俗点将,任何一个守护线程都是整个 JVM 中所有非守护线程的“保姆” Lambda表达式可以由编译器推断并转换包装为常规的代码,因此可以使用更少的代码来实现同样的功能。 有且仅有一个抽象方法的接口,叫做函数式接口。 Lambda表达式要想使用,一定要有函数式接口的推断环境 Lambda的格式就是为了将抽象方法的头,翻译为以下3点: 省略格式:在Lambda表达式中,凡是可以推导的,都可以省略 来看一个例子: 上面的问题的答案是肯定的。这即是通过引用类的方法来实现。分为两种,静态方法和成员方法。 这个lamda表达式 就是对接口 填上了方法体。 进行调用。并没有传参,直接通过方法进行传参。 自定义函数式接口,在第十二章中的最后。 外部类,顾名思义,就是外部的类。定义一个类A,在A的内部再定义一个类B,则A就是外部了类,B就是内部类 内部类一般来说共分为4种:成员(常规)内部类、静态内部类、局部内部类、匿名内部类。 内部原理的区别: 局部内部类等同于局部变量 指的是类没有名字。 如果需要新建一个线程,一种常见的写法是这样: 如果要给一个字符串列表通过自定义比较器,按照字符串长度进行排序,Java 7的书写形式如下: 上述代码通过内部类重载了Comparator接口的compare()方法,实现比较逻辑。采用Lambda表达式可简写如下: 也许你已经想到了,能够使用Lambda的依据是必须有相应的函数接口(函数接口,是指内部只有一个抽象方法的接口)。这一点跟Java是强类型语言吻合,也就是说你并不能在代码的任何地方任性的写Lambda表达式。实际上Lambda的类型就是对应函数接口的类型。Lambda表达式另一个依据是类型推断机制,在上下文信息足够的情况下,编译器可以推断出参数表的类型,而不需要显式指名。Lambda表达更多合法的书写形式如下: 自定义函数接口很容易,只需要编写一个只有一个抽象方法的接口即可。
八大基本类型:byte、short、int、long、float、double、char、boolean
封装类:Byte、Short、Integer、Long、Float、Double、Character、Boolean
优先级:byte
大范围数据类型 变量名=小范围数据类型的值
eg:long=5;
小范围数据类型 变量名=(小范围数据类型)大范围数据类型的值;
eg: int a=(int)5L;
五、运算符
1. 易混淆运算符
a++:先保存旧值,再自增,用旧值参加运算;++a:直接自增,用自增后的值参与运算。2. 位运算符:
3. 三目运算符:
五、语句
六、集合类
1. Collection接口
由于Set集合和List集合都继承了Collection接口,所以这些方法Set和LIst都是能用的。public static void main(String[] args) {
System.out.println("Hello World!");
Collection<String> collection=new ArrayList<String>();
((ArrayList<String>) collection).add("hello");
((ArrayList<String>) collection).add("world");
Iterator<String> iterator=collection.iterator();
while (iterator.hasNext())
{
System.out.println(iterator.next());
}
System.out.println("长度:"+collection.size());
System.out.println("删除hello,还有:");
collection.remove("hello");
Iterator<String> iterator1=collection.iterator();
while (iterator1.hasNext())
{
System.out.println(iterator1.next());
}
System.out.println("再删除world,剩余长度:");
collection.remove("world");
System.out.println("长度"+collection.size());
}
2.List集合
3.补充:数组
Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大,因为这需要重排数组中的所有数据。1) 数组及相关概念。
2) 数组的创建。
格式: 类型名[] 变量名;
如:int[] arr;//定义一个数组a,存放一组int类型的数据
(1). 静态初始化:指定长度和数组元素。
格式:
数据类型[] 数组名 = {元素1,元素2,…};
如:int[] arr= {1,2,3,4,5,6,7,8,9,0,};
(2). 动态初始化:
格式1: 只指定长度,由系统给出初始化值
数据类型[] 数组名 = new 数据类型[数组长度];
如: int[] arr = new int[5];
格式2:指定元素,但[]内不能规定长度。
数据类型[] 数组名=new 数据类型[]{元素1,元素2,元素3,元素4…};
如:int[] arr=new int[]{1,2,3,4,5,6,7,8,9,0};3) 数组的遍历
格式:for (String string : array) {
System.out.println(string);
}
4. 数组的引用。
(1) 数组间的赋值:将一个数组赋值给另一个数组。
PS:由上可得,当一个数组赋值给另一个数组之后,改变其中任意一个数组中元素的值,另一个数组中相应元素的值也会随之变化。
(2) 数组的复制:将一个数组的元素复制到另外一个数组中。
System.arraycopy(src,begin1,des,begin2,length);
Src:源数组,表示被复制的数组
newLength:复制后新数组的长度。
例:
PS:与数组的复制不同,当一个数组复制到另一个数组后,干煸其中一个数组元素的值,不会影响另一个数组元素的值。5) Arrays工具类。
1. Set集合
对于TreeSet,存进treeSet中的对象必须实现Comparable接口,并重写comparaTo(Object obj)方法用来比较此对象与指定对象的顺序,如果小于、等于或大于,则分别返回 负整数、0、1.
案例:import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class Student implements Comparable {
private int id;
private String name;
public Student(int id,String name) {
this.id=id;
this.name=name;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public void setId(int id) {
this.id = id;
}
@Override
public int compareTo(Object o) {
Student student=(Student)o;
return id>student.id? 1:(id==student.id? 0:-1);
}
public static void main(String args[])
{
Student stu1=new Student(001,"张三");
Student stu2=new Student(002,"李四");
Student stu3=new Student(003,"王五");
TreeSet<Student> tree=new TreeSet<Student>();
tree.add(stu1);
tree.add(stu2);
tree.add(stu3);
Iterator it=tree.iterator();
while (it.hasNext())
{
Student student=(Student) it.next();
System.out.println("id:"+student.getId()+" "+"name:"+student.getName());
}
System.out.println("第一个元素:"+"id:"+tree.first().getId()+" "+"name:"+tree.first().getName());
System.out.println("最后一个元素:"+"id:"+tree.last().getId()+" "+"name:"+tree.last().getName());
System.out.println("stu3之前的所有元素");
Iterator<Student> iterator=tree.headSet(stu3).iterator();
while (iterator.hasNext())
{
Student student=(Student) iterator.next();
System.out.println("id:"+student.getId()+" "+"name:"+student.getName());
}
System.out.println("stu2之后的所有元素");
Iterator<Student> iterator1=tree.tailSet(stu2).iterator();
while (iterator1.hasNext())
{
Student student=(Student) iterator1.next();
System.out.println("id:"+student.getId()+" "+"name:"+student.getName());
}
System.out.println("stu1和stu3之间的元素");
Iterator<Student> iterator2=tree.subSet(stu1,stu3).iterator();
while (iterator2.hasNext())
{
Student student=(Student) iterator2.next();
System.out.println("id:"+student.getId()+" "+"name:"+student.getName());
}
}
}
输出结果:
2. 补充:数据结构 Hash表(哈希表)
3. Map集合
Map接口常用方法如下:
案例:import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
public class HashMapTest {
public static void main(String[] args)
{
Map<String,String> stuMap=new HashMap<String, String>();
stuMap.put("001","张阿三");
stuMap.put("003","王阿五");
stuMap.put("002","李阿四");
System.out.println("key为001的值:"+stuMap.get("001"));
if(stuMap.containsKey("001"))
{
System.out.println("存在key为001的元素");
}
else
{
System.out.println("不存在key为001的元素");
}
if(stuMap.containsValue("李阿四"))
{
System.out.println("存在Value为李阿四的元素");
}
else
{
System.out.println("不存在Value为李阿四的元素");
}
System.out.println("所有Key的集合");
Iterator<String> it=stuMap.keySet().iterator();
while (it.hasNext())
{
System.out.println(it.next());
}
System.out.println("所有Value的集合");
Iterator<String> it1=stuMap.values().iterator();
while (it1.hasNext())
{
System.out.println(it1.next());
}
TreeMap<String,String> treeMap=new TreeMap<String, String>();
treeMap.putAll(stuMap);
Iterator<String> it3=treeMap.keySet().iterator();
System.out.println("TreeMap类实现的集合,有序");
while (it3.hasNext())
{
System.out.println(it3.next());
}
}
}
七、集合类问题
1. Array和ArrayList有何区别?
Array是指定大小的,而ArrayList大小是固定的2. ArrayList和LinkedList的区别是什么?
3. 哪些集合类是线程安全的?
一般使用synchronized关键字加锁同步控制,来解决线程不安全问题。ArrayList线程不安全,Vector线程安全;
HashMap线程不安全,HashTable线程安全;
StringBuilder线程不安全,StringBuffer线程安全
Stack也是线程安全的,继承于Vector
ConcurrentHashMap是一种高效但是线程安全的集合。
4. 线程安全和速度之间的取舍?
当不需要线程安全时,可以选择ArrayList,避免方法同步产生的开销;当多个线程操作同一个对象时,可以选择线程安全的Vector;5. List、Set和Map的区别和联系?
Map特点:元素按键值对存储,无放入顺序
Set特点:元素无放入顺序,元素不可重复(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的)
参考资料:list、set和map 的区别6. HashMap的底层结构/工作原理是什么?
HashMap是基于hashing实现的,当我们进行put操作时,根据传递的key值得到它的hashcode,然后再用这个hashcode与数组的长度进行模运算,得到一个int值,就是Entry要存储在数组的位置(下标);当通过get方法获取指定key的值时,会根据这个key算出它的hash值(数组下标),根据这个hash值获取数组下标对应的Entry,然后判断Entry里的key,hash值或者通过equals()比较是否与要查找的相同,如果相同,返回value,否则的话,遍历该链表(有可能就只有一个Entry,此时直接返回null),直到找到为止,否则返回null。
HashMap之所以在每个数组元素存储的是一个链表,是为了解决hash冲突问题,当两个对象的hash值相等时,那么一个位置肯定是放不下两个值的,于是hashmap采用链表来解决这种冲突,hash值相等的两个元素会形成一个链表。7. HashMap和Hashtable的区别?
8. 哪些集合类提供对元素的随机访问?
八、java基础之抽象类和接口
1. 抽象类
public abstract void f(); //没有内容
抽象类 使用 abstract 关键字修饰,即包含抽象方法的类。
如果一个类包含一个或者多个抽象方法,该类必须被限定为抽象的。抽象类可以不包含抽象方法。
接口 是抽象类的一种特殊形式,使用 interface 修饰。public abstract class BaseActivity {
private final String TAG = this.getClass().getSimpleName(); //抽象类可以有成员
void log(String msg){ //抽象类可以有具体方法
System.out.println(msg);
}
// abstract void initView(); //抽象类也可以没有抽象方法
}
抽象类的作用:捕捉子类的通用特性2. 接口
public interface OnClickListener {
void onClick(View v);
}
其成员方法,默认由public abstract修饰的(可省略),且不能有方法体,只是一种声明。interface A{
int var; //错,必须显式初始化
int COUNT = 1; //合法,默认为public static final的
public static final int CON = 2;//合法,显式声明为public static final的
public abstract void testfun();//合法,显式声明为public abstract
void testfun01();//合法,默认为public abstract的
protected void testfun02();//错,接口中的方法的修饰符只能存在public、abstract、 default、 static
void testfun03() {};//错,接口中只能有抽象方法,不能有方法体
static void testfun04() {}//合法,由static修饰的方法,必须有方法体
}
* 可以通过类命名直接访问:ImplementClass.name
* 不推荐使用接口创建常量类
接口不能创建对象,但可以申明一个接口变量,方便调用
完全解耦,可以编写可复用性更好的代码1) 接口是用来干什么的
2) 如何使用接口:
public class interface01 implements A,B{
//子类重写了接口的所有抽象方法
@Override
public void testfun() {
System.out.println("接口A");
}
@Override
public void testfun01() {
System.out.println("接口B");
}
public static void main(String[] args) {
interface01 i1 = new interface01();
//定义接口类型的引用
A a = i1;
B b = i1;
a.testfun();
b.testfun01();
}
}
interface A{
public abstract void testfun();//合法,显式声明为public abstract
}
interface B{
void testfun01();
}
3) 一个接口不能实现其他接口,但是可以继承其他多个接口
interface C extends A,B{
}
4) Instanceof
判断一个对象是否实现了某接口。5) 接口的好处
(2)代码复用,同一套代码可以处理多种不同类型的对象,只要这些对象都有相同的能力。
(3)降低了耦合,提高了灵活性。使用接口的代码依赖的是接口本身,而非实现接口的具体类型,程序可以根据情况替换接口的实现,而不影响接口使用者。6) 可以定义方法体
public default void methodDefault(){
System.out.println();
}
5. 区别
十、Java基础之线程
1. 线程的四种状态
2. 什么是线程?它与进程有什么区别?
线程是指程序在执行过程中,能够执行程序代码的一个执行单元。在 Java 语言中线程有 4 种运行状态:运行、就绪、挂起和结束。
进程是指一段正在执行的程序。而线程有时也被称为轻量级的进程,各个线程之间共享程序的内存空间(代码段、数据段和堆空间)及一些进程级的资源(例如打开文件),但是各个线程拥有自己的栈空间
3. 多线程与单线程的区别
你早上上班,正要打卡的时候,手机响了。。你如果先接了电话,等接完了,在打卡,就是单线程。
如果你一手接电话,一手打卡。就是多线程。
2件事的结果是一样的。。你接了电话且打了卡。4. 如何实现 Java 的多线程
1) 继承 Thread 类,重写 run() 方法
那么如何启动这个线程呢?只需要 new 出 MyThread 的实例,然后调用它的 start() 方法,这样 run() 方法中的代码就会运行在子线程,
如下:new MyThread().start();2) 实现 Runnable 接口,并实现该接口的 run() 方法
如果使用这种写法,启动线程的方法也需要相应的改变,如下:
Thread 构造函数接受一个 Runnable 参数,我们 new 出的 MyThread 正是一个实现了 Runnable 接口的对象,所以可以直接将它传入到 Thread 的构造函数里,接着就和上面的一样了,调用 Thread 的 start() 方法,run() 方法中的代码就会在子线程当中运行了3) 实现 Callable 接口,重写 call 方法
以上代码只供参考,以上 3 种方式中,前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的,当需要实现多线程时,一般推荐实现 Runnable 接口的方式,其原因是:
首先,Thread 类定义了多种方法可以被派生类使用或重写,但是只有 run() 方法是必须被重写的,在 run() 方法中实现这个线程的主要功能,这当然是实现 Runnable 接口所需的方法5. run() 方法与 start() 方法有什么区别?
6. 多线程同步实现的方法有哪些?
1) synchronized 关键字
synchronized 关键字**主要有两种用法(synchronized 方法和 synchronized 块)**,此外该关键字还可以作用于静态方法,或某个实例,但这都对程序的效率有一定的影响
只要把多个线程对类需要被同步的资源的操作放到 Test() 方法中,就能保证这个方法在同一时刻只能被一个线程访问,从而保证了多线程访问的安全,然而当一个线程的方法体规模非常大时,把该方法声明为 synchronized 会大大影响程序的执行效率,为了提高程序的效率,Java 提供了 synchronized 块
2) wait() 方法与 notify() 方法
在 synchronized 代码被执行期间,线程可以调用对象的 wait() 方法,释放对象锁,进入等待状态,并且可以调用 notify() 方法或 notifyAll() 方法通知正在等待的其他线程,notify() 方法仅唤醒一个线程(等待队列中的第一个线程),并容许它去获得锁,notifyAll() 方法唤醒所有等待这个对象的线程,并允许他们去获得锁(并不是让所有唤醒线程都获得锁,而是让他们去竞争)。3) Lock
7. sleep() 方法与 wait() 方法有什么区别?
sleep() 方法是 Thread 类的静态方法,是线程用来控制自身流程的,它会使此线程暂停执行一段时间,而把执行机会让给其他线程,等到计时时间一到,次线程会自动苏醒,而 wait() 方法是 Object 类的方法,用于线程间的通信,这个方法会使当前拥有该对象锁的进程等待,直到其他线程调用 notify() 方法(或 notifyAll() 方法)时才醒来,不过开发人员也可以给他指定一个时间,自动醒来2) 对象锁处理机制不同
3) 使用区域不同
最后由于 sleep() 方法不会释放锁标志,容易导致死锁问题的发生,因此一般情况下不推荐使用,而推荐使用 wait() 方法8. 终止线程的方法有哪些?
9. 什么是守护线程?
用户线程和守护线程几乎一样,唯一的不同之处就在于如果用户线程已经全部退出运行,只剩下守护线程存在,JVM 也就退出了,因为当所有非守护程序结束时,没有了被守护者,守护线程也就没有工作可做了,也就没有运行程序的必要,程序也就终止了,同时会杀死所有守护线程1) 原理不同
十一、java8之lamda表达式
Java中使用Lambda表达式的前提是:必须有“函数式接口”
线程中的Runnable也是一个函数式接口,其中的 run 方法就是一个函数式接口
1. 函数式接口
注意:
如何万无一失地检测当前接口是不是函数式接口呢? 用一个固定的格式写在public interface之前一行即可:
如下面代码定义的接口就是函数式接口:只有一个抽象方法。
2. Lambda表达式标准接口
3. 例子:
4. Lambda表达式的冗余
首先定义一个类PrintClass,这个类有一个静态方法print,用于打印给定的字符串:
然后我定义了一个函数式接口PrintMsg :
在main函数里我用Lambda表达式指定了接口的作用:
运行结果如图:
是我们想要的结果。但是我们发现,在我们定义的类PrintClass里的静态方法printStr已经实现了这个功能,但是在main里又通过Lambda表达式重新写了一遍,这没有必要,能不能通过Lambda表达式直接调用已有的方法呢?5. Lambda引用类的静态方法和成员方法
对于静态方法,通过 类名::静态方法名;
对于成员方方法,通过 类的对象名::成员方法名;
还是上面的例子:
6. 自己观后感
十二、Java之外部类、内部类
1. 外部类
2. 内部类
或者说分为 静态内部类和非静态内部类(成员(常规)内部类、局部内部类、匿名内部类)。// 静态内部类
public class School {
private static School instance = null;
static class Teacher {}
}
// 非静态内部类:成员内部类
public class School { private String name;
class Teacher {}
}
1) 静态内部类和非静态内部类之间的区别主要如下:
private static Singleton instance = null;2、创建方式的区别:
new School.Teacher();
School s = new School;
s.Teacher t = s.new Teacher()。
2) 静态内部类
public class School {
private static School instance = null;
static class Teacher {}
}
3) 成员内部类
4) 局部内部类(成员方法中的类)
重点:局部内部类在访问局部变量的时候,局部变量必须使用final修饰5) 匿名内部类
使用匿名内部类实现:少定义一个类(实现的那个类)
6) 典型的匿名内部类的使用并和lamda表示式关系
a) 例子1:无参函数的简写
上述代码给Tread类传递了一个匿名的Runnable对象,重载Runnable接口的run()方法来实现相应逻辑。这是JDK7以及之前的常见写法。匿名内部类省去了为类起名字的烦恼,但还是不够简化,在Java 8中可以简化为如下形式:
上述代码跟匿名内部类的作用是一样的,但比匿名内部类更进一步。这里连接口名和函数名都一同省掉了,写起来更加神清气爽。b) 例子2:带参函数的简写
上述代码跟匿名内部类的作用是一样的。除了省略了接口名和方法名,代码中把参数表的类型也省略了。这得益于javac的类型推断机制,编译器能够根据上下文信息推断出参数的类型,当然也有推断失败的时候,这时就需要手动指明参数类型了。注意,Java是强类型语言,每个变量和对象都必需有明确的类型。c) 简写的依据
上述代码中,1展示了无参函数的简写;2处展示了有参函数的简写,以及类型推断机制;3是代码块的写法;4和5再次展示了类型推断机制。d) 自定义函数接口
上面代码中的**@FunctionalInterface是可选的,但加上该标注编译器会帮你检查接口是否符合函数接口规范**。就像加入@Override标注会检查是否重载了函数一样。
有了上述接口定义,就可以写出类似如下的代码:
进一步的,还可以这样使用:
3. 内部类与外部类的联系
十三、函数式接口的三种实现方式
1. 第一种:实现接口方式
2. 第二种:匿名内部类
3. 第三种:lamada表达式