Java知识汇集(2)

1、多线程


线程与进程的区别

多个进程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系统资源,有可能互相影响.
?线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。

多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。

了解一下java在多线程中的基础知识

1.Java中如果我们自己没有产生线程,那么系统就会给我们产生一个线程(主线程,main方法就在主线程上运行),我们的程序都是由线程来执行的。

2. 进程:执行中的程序(程序是静态的概念,进程是动态的概念)。

3. 线程的实现有两种方式,第一种方式是继承Thread类,然后重写run方法;第二种是实现Runnable接口,然后实现其run方法。

4.
将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法。当某个类继承了Thread类之后,该类就叫做一个线程类。

5. 一个进程至少要包含一个线程。

6. 对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行)。

7. 对于双核或双核以上的CPU来说,可以真正做到微观并行。

Thread源码研究:

1) Thread类也实现了Runnable接口,因此实现了Runnable接口中的run方法;

2)
当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该number将是自动增加的,并被所有的Thread对象所共享(因为它是static的成员变量)。

3) 当使用第一种方式来生成线程对象时,我们需要重写run方法,因为Thread类的run方法此时什么事情也不做。

4)当使用第二种方式生成线程对象时,我们需要实现Runnable接口的run方法,然后使用new Thread(new
MyThread())(假如MyThread已经实现了Runnable接口)来生成线程对象,这时的线程对象的run方法或调就会MyThread类的run方法,这样我们自己编写的run方法就执行了。

说明:

Public void run(){

If(target!=null){

Target.run();

}}

当使用继承Thread生成线程对象时,target为空,什么也不执行,当使用第二种方式生成时,执行target.run(),target为runnable的实例对象,即为执行重写后的方法

总结:两种生成线程对象的区别:

1.两种方法均需执行线程的start方法为线程分配必须的系统资源、调度线程运行并执行线程的run方法。

2.在具体应用中,采用哪种方法来构造线程体要视情况而定。通常,当一个线程已继承了另一个类时,就应该用第二种方法来构造,即实现Runnable接口。

此外,多线程还有一部分并发和同步的内容,这个就不讲了。

推荐资料:http://www.cnblogs.com/dennisit/p/3690378.html

http://blog.csdn.net/csh624366188/article/details/7318245

2、泛型

泛型(Generic type 或者 generics)是对 Java
语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。通俗的说,就是泛泛的指定对象所操作的类型,而不像常规方式一样使用某种固定的类型去指定。泛型的本质就是将所操作的数据类型参数化,也就是说,该数据类型被指定为一个参数。这种参数类型可以使用在类、接口以及方法定义中。

泛型类中的类型参数几乎可以用于任何可以使用类名的地方。例如,下面是 java.util.Map 接口的定义的摘录:

public interface Map<K, V> {

public void put(K key, V value);

public V get(K key);

}

在使用泛型时,请注意其使用规则和限制,如下:

1、泛型的参数类型只能是引用类型,而不能是简单类型。比如,<int>是不可使用的。

2、可以声明多个泛型参数类型,比如<T, P,Q…>,同时还可以嵌套泛型,例如:<List<String>>

3、泛型的参数类型可以使用extends语句,例如<T extends collection>。

使用extends语句将限制泛型参数的适用范围。这里<T extends collection>
,则表示该泛型参数的使用范围是所有实现了collection接口的calss。如果传入一个<String>则程序编译出错。

4、泛型的参数类型可以使用super语句,例如< T super List >。

super语句的作用与extends一样,都是限制泛型参数的适用范围。区别在于,super是限制泛型参数只能是指定该class的上层父类。这里表示该泛型参数只能是List和List的上层父类。

5、泛型还可以使用通配符,例如<? extends ArrayList>

使用通配符的目的是为了解决泛型参数被限制死了不能动态根据实例来确定的缺点。

举个例子:public class SampleClass < T extends S> {…}

假如A,B,C,…Z这26个class都实现了S接口。我们使用时需要使用到这26个class类型的泛型参数。那实例化的时候怎么办呢?依次写下

SampleClass<A> a = new SampleClass();

SampleClass<B> a = new SampleClass();

SampleClass<Z> a = new SampleClass();

这显然很冗余,还不如使用Object而不使用泛型,呵呵,是吧?

别着急,咱们使用通配符,就OK了。

SampleClass<? Extends S> sc = new SampleClass();

只需要声明一个sc变量,很方便把!

到目前为止,Java 类库中泛型支持存在最多的地方就是集合框架。所有的标准集合接口都是泛型化的 ——
Collection<V>、List<V>、Set<V> 和
Map<K,V>。类似地,集合接口的实现都是用相同类型参数泛型化的,所以 HashMap<K,V> 实现 Map<K,V>
等。

集合类也使用泛型的许多“技巧”和方言,比如上限通配符和下限通配符。例如,在接口 Collection<V> 中,addAll
方法是像下面这样定义的:

interface Collection<V> {

boolean addAll(Collection<? extends V>);

}

该定义组合了通配符类型参数和有限制类型参数,允许您将 Collection<Integer> 的内容添加到
Collection<Number>。





类的泛型

A B 处分别应该填什么?

① interface Hungry<E>{

void munch(E x);

}

② abstract class Animal{}

③ abstract class Plant{}

④ class Grass extends Plant{}

⑤ interface Carnivore<E extends __A__> extends
Hungry<E>{}

⑥ interface Herbivore<E extends __B__> extends
Hungry<E>{}

⑦ class Sheep extends Animal implements
Herbivore<Grass>{

public void munch(Grass x){}

}

⑧ class Wolf extends Animal implements
Carnivore<Sheep>{

public void munch(Sheep x){}

}

分析:这是一道典型的泛型例子,咋一看挺复杂,只要理清了思路,其实也很简单。先看⑦行,可知⑥行中的E =
Grass;再看④行,可知B = Plant。同样由⑧行可知⑤行中的E = Sheep,再由⑦行可得出 A = Animal。

推荐资料:http://www.cnblogs.com/panjun-Donet/archive/2008/09/27/1300609.html

http://www.cnblogs.com/Fskjb/archive/2009/08/23/1552506.html

3、反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

Java反射机制主要提供了以下功能:
在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。

具体看例子:




import java.lang.reflect.Constructor;

class Person{

public Person() {

}

public Person(String name){

this.name=name;

}

public Person(int age){

this.age=age;

}

public Person(String name, int
age) {

this.age=age;

this.name=name;

}

public String getName() {

return
name;

}

public int getAge() {

return
age;

}

@Override

public String toString(){

return
"["+this.name+"  "+this.age+"]";

}

private String name;

private int age;

}

class hello{

public static void main(String[]
args) {

Class<?>
demo=null;

try{

demo=Class.forName("Reflect.Person");

}catch
(Exception e) {

e.printStackTrace();

}

Person
per1=null;

Person
per2=null;

Person
per3=null;

Person
per4=null;

//取得全部的构造函数

Constructor<?>
cons[]=demo.getConstructors();

try{

per1=(Person)cons[0].newInstance();

per2=(Person)cons[1].newInstance("abc");

per3=(Person)cons[2].newInstance(10);

per4=(Person)cons[3].newInstance("abc",20);

}catch(Exception
e){

e.printStackTrace();

}

System.out.println(per1);

System.out.println(per2);

System.out.println(per3);

System.out.println(per4);

}

}

推荐资料:http://blog.csdn.net/csh624366188/article/details/7309435

http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html

4、Socket

网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。

但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。

Server端Listen(监听)某个端口是否有连接请求,Client端向Server
端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client
端都可以通过Send,Write等方法与对方通信。

对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:

(1) 创建Socket;

(2) 打开连接到Socket的输入/出流;

(3) 按照一定的协议对Socket进行读/写操作;

(4)
关闭Socket.(在实际应用中,并未使用到显示的close,虽然很多文章都推荐如此,不过在我的程序中,可能因为程序本身比较简单,要求不高,所以并未造成什么影响。

java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用很方便。

客户端发送消息到服务端:




Socket clientToServer = new Socket(“192.168.1.1”, 0000);

//建立连接到socket的PrintWtiter

//字符数据与字节间的转换桥梁,可以连接String和Socket两端

PrintWriter writer = new
PrintWriter(clientToServer.getOutputStream());

//写入数据

Writer.println(“***”);

客户端从服务端读取消息:




Socket serverToClient = new Socket(“192.168.1.1”, 0000);

//建立连接到socket上低层输入串流的InputStreamReader

InputStreamReader stream = new
InputStreamReader(serverToClient.getInputStream());

//建立BufferReader来读取

BufferReader reader = new BufferReader(stream);

String message = reader.readLine();

推荐资料:Head First Java P471-485

5、垃圾回收

Java 语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管
理。由于有个垃圾回收机制,Java中的对象不再有"作用域"的概念,只有对象的引用才有"作用域"。垃圾回收可以有效的防止内存泄露,有效的使用可以使
用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收,程序员不能
实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。

判断对象是否该被回收算法

1.引用计数算法

给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器值就减1;任何时刻计数器值都为0时对象就表示它不可能被使用了。这个算法实现简单,但很难解决对象之间循环引用的问题,因此Java并没有用这种算法!这是很多人都误解了的地方。

2.根搜索算法

通过一系列名为“GC ROOT”的对象作为起始点,从这些结点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC
ROOT没有任何引用链相连时,则证明这个对象是不可用的。如果对象在进行根搜索后发现没有与GC
ROOT相连接的引用链,则会被第一次第标记,并看此对象是否需要执行finalize()方法(忘记finalize()这个方法吧,它可以被try-finally或其他方式代替的),当第二次被标记时,对象就会被回收。

三种垃圾回收器

1.串行收集器

使用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高。但是,也无法使用多处理器的优势,所以此收集器适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。可以使用-XX:+UseSerialGC打开。

2.并行收集器

对年轻代进行并行垃圾回收,因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。使用-XX:+UseParallelGC.打开。

3.并发收集器

可以保证大部分工作都并发进行(应用不停止),垃圾回收只暂停很少的时间,此收集器适合对响应时间要求比较高的中、大规模应用。使用-XX:+UseConcMarkSweepGC打开。

推荐资料:http://blog.csdn.net/csh624366188/article/details/8042649

http://www.cnblogs.com/xwdreamer/archive/2012/05/06/2485473.html

http://www.cnblogs.com/gw811/archive/2012/10/19/2730258.html

6、内存管理

推荐资料:http://www.cnblogs.com/gw811/archive/2012/10/18/2730117.html

http://hllvm.group.iteye.com/group/wiki/3053-JVM

7、Java8之Lambda表达式

Java 8的另一大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。

首先看看在老版本的Java中是如何排列字符串的:




List<String> names = Arrays.asList("peter", "anna", "mike",
"xenia");

Collections.sort(names, new Comparator<String>() {

@Override

public int compare(String a, String b) {

return b.compareTo(a);

}

});

只需要给静态方法 Collections.sort
传入一个List对象以及一个比较器来按指定顺序排列。通常做法都是创建一个匿名的比较器对象然后将其传递给sort方法。

在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,lambda表达式:




Collections.sort(names, (String a, String b) -> {

return b.compareTo(a);

});

看到了吧,代码变得更段且更具有可读性,但是实际上还可以写得更短:




Collections.sort(names, (String a, String b) -> b.compareTo(a));

对于函数体只有一行代码的,你可以去掉大括号{}以及return关键字,但是你还可以写得更短点:





Collections.sort(names, (a, b) -> b.compareTo(a));

Java编译器可以自动推导出参数类型,所以你可以不用再写一次类型。

推荐资料:http://www.cnblogs.com/wxfvm/p/3676730.html

8、Java8之函数式接口

Java 8
引入的一个核心概念是函数式接口。如果一个接口定义个唯一一个抽象方法,那么这个接口就成为函数式接口。比如,java.lang.Runnable就是一个函数式接口,因为它只顶一个一个抽象方法:

public abstract void run();

留意到“abstract”修饰词在这里是隐含的,因为这个方法缺少方法体。为了表示一个函数式接口,并非想这段代码一样一定需要“abstract”关键字。

默认方法不是abstract的,所以一个函数式接口里可以定义任意多的默认方法,这取决于你。

同时,引入了一个新的Annotation:@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口。编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。

推荐资料:http://www.oschina.net/translate/everything-about-java-8

9、Java8之Stream

java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream
操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream
的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set,
Map不支持。Stream的操作可以串行执行或者并行执行。

首先看看Stream是怎么用,首先创建实例代码的用到的数据List:




List<String> stringCollection = new ArrayList<>();

stringCollection.add("ddd2");

stringCollection.add("aaa2");

stringCollection.add("bbb1");

stringCollection.add("aaa1");

stringCollection.add("bbb3");

stringCollection.add("ccc");

stringCollection.add("bbb2");

stringCollection.add("ddd1");

过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。




stringCollection

.stream()

.filter((s) -> s.startsWith("a"))

.forEach(System.out::println);

// "aaa2", "aaa1"

排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。




stringCollection

.stream()

.sorted()

.filter((s) -> s.startsWith("a"))

.forEach(System.out::println);

// "aaa1", "aaa2"

需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的

中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。





stringCollection

.stream()

.map(String::toUpperCase)

.sorted((a, b) -> b.compareTo(a))

.forEach(System.out::println);

// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"

Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。




boolean anyStartsWithA =

stringCollection

.stream()

.anyMatch((s) ->
s.startsWith("a"));

System.out.println(anyStartsWithA);      //
true

boolean allStartsWithA =

stringCollection

.stream()

.allMatch((s) ->
s.startsWith("a"));

System.out.println(allStartsWithA);      //
false

boolean noneStartsWithZ =

stringCollection

.stream()

.noneMatch((s) ->
s.startsWith("z"));

System.out.println(noneStartsWithZ);      //
true

计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。





long startsWithB =

stringCollection

.stream()

.filter((s) ->
s.startsWith("b"))

.count();

System.out.println(startsWithB);    // 3

这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的:




Optional<String> reduced =

stringCollection

.stream()

.sorted()

.reduce((s1, s2) -> s1 +
"#" + s2);

reduced.ifPresent(System.out::println);

// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"

推荐资料:http://datalab.intyt.com/archives/370

时间: 2024-10-16 13:13:53

Java知识汇集(2)的相关文章

Java知识汇集(1)

由于一些原因需要整理一些Java的知识,把整理出来的结果分享一下. 1.三大基本特性 我们以Java的三大基本特性为角度展开 封装.继承.多态 封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口.面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治.封装的对象,这些对象通过一个受保护的接口访问其他对象.封装主要实现了隐藏细节,对用户提供访问接口,无需关心方法的具体实现. Java的权限控制等级由大到小依次为:public.protected.(default).p

java 知识收集

1,若方法传入的对象参数为空,则在方法中改变参数并不会改变声明的对象 public void setList(List<String> list){ list = new ArrayList<String>() ; } @Test public void t4(){ List<String> list = null ; setList(list); System.out.println(list );//输出 null } 2,不要再foreach遍历的时候删除 jav

java知识查漏补缺

一.重写(override)和重载(overload)的区别 二者除了名字相似,其实没什么联系 范围不同:重写发生在同一个类的不同方法之间.重载发生在父类和子类自荐. 前提: 重写要求:方法名相同,参数列表不同,对于返回值类型不要求相同. 重载要求:方法名形同,参数列表也相同.重载是实现多态的关键,注意如果父类中的方法是private类型,那么子类中对应方法不算重载,而相当于是定义了一个新方法. 二.final的用法 修饰类:该类不能被继承 修饰方法:该方法不能被重写 修饰属性:该属性初始化后不

Java知识简介与环境变量配置问题

一.在学习一门语言中,不仅需要掌握其语法结构,开发平台以及环境也是很重要的.在开始Java学习之前首先对其进行压缩包的下载安装,以及开发平台环境下载安装.基于此下面文章就此展开: 1.        下载并安装JDK包,然后进行快速正确安装操作.(具体安装步骤网上很多,不在赘述.) 2.        安装成功后,需要对其进行环境变量配置,这一点有点特别,特别针对C语言.C#语言开发的同学更应该注意. 环境变量配置步骤如下: 打开:计算机属性->高级->环境变量选项卡->系统变量->

再看静态static及相关知识汇集

在C#程序中,没有全局变量的概念,这意味着所有的成员变量只有该类的实例才能操作这些数据,这起到了“信息隐藏”的作用.但有些时候,这样做却不是个明智的选择.C#中静态变量——它在类中类似全局变量,保存类的公共信息,所有该类(而非对象或实例)共享该值. 静态构造函数——static修饰的构造函数,静态构造函数是C#的一个新特性,在编程过程中用处并不广,它的主要目的是用于初始化一些静态的变量.因为这个构造函数是属于类的,而不属于任何一个实例,所以这个构造函数只会被执行一次,而且是在创建此类的第一个实例

java在线聊天项目 实现基本聊天功能后补充的其他功能详细需求分析 及所需要掌握的Java知识基础

补充聊天项目功能,做如下需求分析: 梳理项目开发所需的必要Java知识基础 GUI将使用更快速的swt实现 SWT(Standard Widget Toolkit) Standard Widget Toolkit是一个开源的GUI编程框架,与AWT/Swing有相似的用处,著名的开源IDE-eclipse就是用SWT开发的. 在SWT之前,Sun已经提供了一个跨平台GUI开发工具包就是AWT (Abstract Windowing Toolkit). swt开发包下载地址 链接: https:/

Java知识图谱(附:阿里Java学习计划)

摘要:     本文主要描绘了Java基础学习过程,给出Java知识结构图,以及阿里Java岗学习计划,对Java学习爱好者.准备及将要从事Java开发方面的同学大有裨益. 温馨提示:     由于CSDN对图片的压缩,导致图片观看效果不理想,建议大家通过右键"在新标签页打开图片"进行观看或直接把图片下载下来观看. JVM 1. 内存模型( 内存分为几部分? 堆溢出.栈溢出原因及实例?线上如何排查?) 2. 类加载机制 3. 垃圾回收 Java基础 什么是接口?什么是抽象类?区别是什么

Java知识回顾 (11) 异常处理

距离最近的 Java知识回顾系列(10),2019.4.24日,到现在,已经近半年过去了. 感觉,做一件事情,如果有头无尾,实在不好,心里会一直悬着.所以,现在继续上面的内容. 再次声明,正如(1)中所描述的,本资料来自于runoob,略有修改. 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error. 异常发生的原因有很多,通常包含以下几大类: 用户输入了非法数据. 要打开的文

Java知识树梳理

Java知识树梳理 1.前端 HTML CSS JavaScript 2.后台 Java基础 算法 web方面 分布式.中间件.服务器等方面 大数据方面 3.数据库 原文地址:https://www.cnblogs.com/Lonnn/p/11727752.html