Java基础知识(二)

1,字符串

new String(“abc”)创建了几个对象?

一个或两个,如果常量池中原来有“abc”,则只创建一个对象;如果常量池中原来没有字符串“abc”,那么就会创建两个对象。

String s="abc";
String s1="ab"+"c";
System.out.println(s==s1);
输出 true ,因为"ab"+"c"在编译器就被转换为"abc",存放在常量区,因此输出结果为true。

2,Java定义了一个基类(java.lang.Throwable)作为所有异常的父类。Error、Exception、RuntimeException都是Throwable的子类,因此都能使用throw抛出。

Java中,常见的运行时异常(runtime exception)有:NullPointerException、ClassCastException、ArrayIndexOutOfBoundsException、ArrayStoreException、BufferOverflowException、ArithmeticException等。。

运行时异常不能用try catch 捕获。

3,Java IO流 流的主要作用是 为了改善程序性能且使用方便。

分为字节流 和字符流。

字节流以字节(8bit)为单位,包含两个抽象类:InputStream输入流 和 OutputStream输出流。

字符流以字符(16bit)为单位,一次可以读取多个字节,包含两个抽象类:Reader输入流 和 Writer输出流。

字节流和字符流最主要的区别是: 字节流在处理输入输出时不会用到缓存,而字符流用到了缓存。

Java IO类在设计时用到了 Decorator(装饰者)模式。

4,NIO(NonBlocking IO)和IO的区别

Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读 取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该 缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

阻塞与非阻塞IO

Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻 塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

选择器(Selectors)

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

5,Socket通信

常见笔试题

打开ServerSocket连接,
accept 接受连接
read 读取数据
send 发送数据
close 关闭连接
服务器端:

package Java基础知识.Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
public class Server {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        BufferedReader br=null;
        PrintWriter pw=null;
        try{
            ServerSocket server=new ServerSocket(2000);
            Socket socket=server.accept();
            //获取输入流
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //获取输出流
            pw = new PrintWriter(socket.getOutputStream(),true);
            String s=br.readLine();//获取接收的数据
            pw.println(s);//发送相同的数据给客户端
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                br.close();
                pw.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

}

客户端:

package Java基础知识.Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.*;
public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        BufferedReader br=null;
        PrintWriter pw=null;
        try{
            Socket socket=new Socket("localhost",2000);
            //获取输入流与输出流
            br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            pw=new PrintWriter(socket.getOutputStream(),true);
            //向服务器发送数据
            pw.println("Hello ");
            String s=null;
            while(true){
                s=br.readLine();
                if(s!=null){
                    break;
                }
            }
            System.out.println(s);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                br.close();
                pw.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

}

在非阻塞IO (NIO)出现之前,Java通过传统的Socket实现基本的网络通信功能,

NIO采用轮询的方式在处理多线程请求时不需要上下文的切换,而采用多线程的实现方式在线程之间切换时需要上下文的切换,同时也需要进行压栈和出栈的操作,因此,NIO有较高的执行效率。

且NIO采用Buffer保存数据(如ByteBuffer、CharBuffer等),简化了对流数据的管理。

NIO在网络编程中有着非常重要的作用,与传统的Socket方式相比,由于NIO采用了非阻塞的方式,在处理大量并发请求时,使用NIO要比Socket效率高出很多。

6,Java序列化

两种方式:实现Serializable接口(writeObject、readObject)、Externalizable(writeExternal、readExternal)

声明为static或者transient的数据成员不能被序列化。即Java在序列化时不会实例化被static或者transient修饰的变量

因为:static代表类的成员,transient(透明的,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持,代表对象的临时数据)。

什么情况下需要序列化(将对象转化为流 ,便于传输):

1)需要通过网络来发送对象,或对象的状态需要被持久化到数据库或文件中。

2)序列化能实现深复制,即可用复制引用的对象。

package Java基础知识;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
public class 序列化  implements Serializable{
    private String name;
    private int age;
    public 序列化(){
        this.name=name;
        this.age=age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        序列化 p=new 序列化();
        ObjectOutputStream oos=null;
        ObjectInputStream ois=null;
        try{
            FileOutputStream fos=new FileOutputStream("perple.out");
            oos=new ObjectOutputStream(fos);
            oos.writeObject(p);
            oos.close();
        }catch(Exception e){
            序列化 p1;
            try{
                FileInputStream fis=new FileInputStream("perple.out");
                ois=new ObjectInputStream(fis);
                p1=(序列化)ois.readObject();
                System.out.println("name:"+p1.getName());
                System.out.println("age:"+p1.getAge());
                ois.close();
            }catch(Exception ex){
                ex.printStackTrace();
            }

        }
    }

}

反序列化(将流转化为对象)

一个Java程序运行 从上到下的环境次序 是 :

Java程序、JRE/JVM、操作系统、硬件。

java文件被 javac指令编译为 .class后缀的 字节码 文件, 再由JVM执行。java程序经编译后会产生字节码。。

类加载步骤:

1)装载。 找到对应的class文件,然后导入

2)检查 检查待加载的class文件的正确性。

3)准备。 给类中的静态变量分配存储空间。

4)解析。给符号引用转换成直接引用。

5)初始化。 对静态变量和静态代码块 执行初始化工作。

7,垃圾回收(GC Garbage Collection)

回收程序中不再使用的内存。

垃圾回收器负责完成3项任务: 分配内存、确保被引用对象的内存不被错误地回收以及回收不再被引用的对象的内存空间。

只要有一个以上的变量引用该对象,该对象就不会被回收。

垃圾回收器,使用有向图来记录和管理内存中的所有对象,通过有向图来识别哪些对象是“可达的”(有变量引用它就是“可达的”),所有“不可达”对象都是可被垃圾回收的。。

垃圾回收算法:

1)引用计数算法:效率较低,JVM没有采用。

2)追踪回收算法

3)压缩回收算法

4)复制回收算法

5)按代回收算法

是否可以主动通知JVM进行垃圾回收?

不能实时地调用垃圾回收器对某个对象或所有对象进行垃圾回收。可以通过System.gc()方法来“通知”垃圾回收器运行,当然,JVM不会保证垃圾回收器马上就会运行。

8,内存泄漏

有两种情况:一,在堆中申请的空间没有被释放:

二,对象已不再被使用,还依然在内存中保留着。Java中内存泄漏主要为第二种情况。

内存泄漏的原因:

1)静态集合类。 如HashMap、 Vector 。 它们 的生命周期与程序一致,容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。

2)各种连接、 如数据库连接、网络连接 以及 IO连接等。 不再使用时,需要调用close 方法来释放与数据库的连接。

3)监听器

4)变量不合理的作用域。

9, 容器

Collection : Set 、 List

Map

1)Set接口有两个实现类: HashSet 、TreeSet (实现了SortedSet接口,TreeSet容器中的元素是有序的)

2)List 接口的实现类 : LinkedList 、ArrayList 、Vector

3)Map 接口的实现类 : HashMap 、 TreeMap、LinkedHashMap、 WeakHashMap、IdentityHashMap。

HashMap是基于散列表实现的,采用对象的HashCode 可以进行快速查询。 LinkedHashMap 采用列表来维护内部的顺序。 TreeMap基于红黑树的数据结构来实现的,内部元素是按需排列的。

10,迭代器

1)使用容器的迭代器 iterator()方法 返回一个Iterator, 然后通过Iterator 的next()方法返回第一个元素。

2)使用Iterator的 hasNext()方法判断容器中是否还有元素,如果有,可以使用next()方法获取下一个元素。

3)可以通过remove()方法删除迭代器返回的元素。

使用iterator()方法时经常会遇到 ConcurrentModificationException异常,通常是由于使用Iterator遍历容器的同时 又对容器进行增加或者删除的操作导致的,或者由于多线程操作导致。

单线程下的解决办法: 在遍历的过程中把需要删除的对象保存到一个集合中,等遍历结束后再调用removeAll()方法来删除,或者使用 iter.remove()方法。

多线程的解决办法: 使用线程安全容器代替非线程安全的容器,如 ConcurrentHashMap、 CopyOnWriteArrayList等,也可以把对容器的操作放到 synchronized 代码块中,

Iterator 和 ListIterator的区别:

Iterator 只能正向遍历集合,适用于获取、移出元素。 ListIterator 继承自 Iterator,专门针对 List,可以从两个方向来遍历 List,同时支持元素的修改。

11, ArrayList、Vector、LinkedList的区别

ArrayList、Vector:

支持用下标访问元素,同时索引数据的速度比较快,但是在插入删除元素时需要移动元素,执行比较慢。ArrayList、Vector都有一个初始化的容量,Vector 每次默认扩充为原来的2倍,ArrayList默认扩充为原来的1.5倍。

区别在于 : Vector是线程安全的,而ArrayList 是线程非安全的,

LinkedList 采用双向链表实现,插入删除效率高,非线程安全。

12,HashMap、HashTable、ConcurrentHashMap的区别:

ConcurrentHashMap 也是一个基于散列的Map ,但它使用了一种完全不同的加锁策略来提供更高的并发性和伸缩性。

ConcurrentHashMap 并不是将每个方法都在同一个锁上同步并使得每次只能有一个线程访问容器,而是使用一种粒度更细的加锁机制来实现更大程度的共享,这种机制称为 分段锁, 在这种机制中,任意数量的读取线程可以并发地访问Map, 执行读取操作的线程和执行写入操作的线程可以并发地访问Map,

ConcurrentHashMap 带来的结果是: 在并发访问环境下将实现更高的吞吐量,而在单线程环境中只损失非常小的性能。不会抛出ConcurrentModificationException,因此不需要在迭代过程中对容器加锁。在ConcurrentHashMap 中没有实现对Map以提供独占访问。在Hashtable和 synchronizedMap中,获得Map的锁能防止其他线程访问这个Map,

大多数情况下,ConcurrentHashMap 来代替同步Map能进一步提高代码的可伸缩性,只有当应用程序需要加锁Map以进行独占访问时,才应该放弃使用ConcurrentHashMap 。

1,尽量将域声明为final类型,除非需要它们是可变的。

2,不可变对象一定是线程安全的。

不可变对象能极大地减低并发编程的复杂性,它们更为简单而且安全,可以任意共享而无须使用加锁或保护性复制等机制。

3,用锁来保护每个可变变量。

4,当保护同一个不变性条件中所有变量时,要使用同一个锁。

5,如果从多个线程中访问同一个可变变量时没有同步机制,那么程序会出现问题。

Map处理冲突的方法: 开放地址法、再hash法、链地址法等,

HashMap使用的是 链地址法来解决冲突,

从HashMap 中通过key 查找value时,首先调用key 的hashCode()方法来获取到key 对应的hash值 h,确定键为key 的所有值存储的首地址,如果 h 对应的key值有多个,那么程序会接着遍历所有key, 通过调用 key的equals()方法来判断 key的内容是否相等,只有当 equals方法返回值为 true时,对应的value才是正确的结果。

13, Collection 和Collections的区别

Collection 是一个集合接口,主要 List 和Set,提供了对集合对象进行基本操作的通用接口方法。

Collections 是针对集合类的 一个包装类。提供一系列的静态方法,Collections类不能实例化。

多线程

1,线程和进程的区别

线程的4种状态: 运行、就绪、挂起、结束。

一个进程可以拥有多个线程,各个线程之间共享程序的内存空间(代码段、数据、堆空间、及一些进程级的资源(如打开的文件)),但是各个线程拥有自己的栈空间,

1)使用多线程减少程序的响应时间。

2)与进程相比,线程的创建和切换开销更小,便于数据共享。

3)使用多线程能简化程序结构,使程序便于理解和维护

2,实现多线程的方法;

1)继承 Thread类, 复写 run方法

2)实现runnable 接口,复写run方法

3,start()方法和run()方法的区别

系统通过调用线程类的start()方法来启动一个线程,只有通过调用线程类的start()方法才能真正实现多线程。

将一个 用户线程设置为守护线程的方法就是在调用start()方法之前调用对象的setDaemon(true)方法。 若以上参数设置为false, 则表示的是用户进程模式。

join()方法: 让调用该方法的线程在执行完run()方法后,再执行join方法后面的代码。

时间: 2024-12-25 01:01:51

Java基础知识(二)的相关文章

Java基础知识二次学习-- 第一章 java基础

基础知识有时候感觉时间长似乎有点生疏,正好这几天有时间有机会,就决定重新做一轮二次学习,挑重避轻 回过头来重新整理基础知识,能收获到之前不少遗漏的,所以这一次就称作查漏补缺吧!废话不多说,开始! 第一章  JAVA简介 时间:2017年4月24日10:23:32 章节:01章_02节 内容:jdk的配置与安装 完成情况:已经完成,cmd中javac提示出相关命令 时间:2017年4月24日10:30:39 章节:01章_04节 内容:输出HelloWorld 完成情况: 已经完成 javac先将

Java基础知识二次学习-- 第二章 基础语法与递归补充

第二章 基础语法与递归补充   时间:2017年4月24日10:39:18 章节:02章_01节,02章_02节 视频长度:49:21 + 15:45 内容:标识符,关键字与数据类型 心得:由字母,下划线,$,数字组成,应该由字母,下划线$开头,同时应该避开java保留字符 变量是内存中的一小块区域,使用变量名来访问这块区域 执行过程中的内存管理(疑问:这里的内存和Jvm的一样吗?) code segment 存放代码 data segment 静态变量 字符串常量 stack 栈 局部变量 h

Java基础知识二次学习--第五章 数组

第五章 数组 时间:2017年4月26日15:11:30~2017年4月26日15:15:54 章节:05章_01节  视频长度:09:30 内容:一维数组的内存分析 心得: Java中数组是引用类型 栈里面存的数组的引用 实际对象在堆内存里面 (C与C++是分配在栈里的) 内存图: 元素为引用数据类型的数组 引用类型的数组如上图 时间:2017年4月26日15:16:22~2017年4月26日15:19:00 章节:05章_02节  视频长度:05:25 内容:数组元素的创建与使用 心得: 首

Java 基础知识(二)之面向对象技术

1.    面向对象与面向过程的区别 面向对象把数据及对数据的操作方法放在一起,作为一个互相依存的整体,即对象.对同类对象抽象出其共性,即类,类中的大多数数据,只能被本类的方法进行处理.类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信.程序流程由用户在使用中决定. 面向过程是一种以事件为中心的开发方法,就是自顶向下顺序执行,逐步求精,其程序结构是按功能划分为若干个基本模块,这些模块形成一个树状结构,各模块之间的关系也比较简单,在功能上相对独立,每一模块内部一般是由顺序.选择

Java基础知识二次学习--第四章 异常

第四章 异常处理 时间:2017年4月26日11:16:39~2017年4月26日11:28:58 章节:04章_01节 04章_02节 视频长度:20:46+01:16 内容:异常的概念 心得: Java异常是JAVA提供的用于处理程序中错误的一种机制 实在运行的过程中发生的一些异常事件(比如除0溢出,数组越界,要读取的文件不存在) 设计良好的程序应该在异常发生时提供处理这些错误的方法,使得程序不会因为异常的发生而阻断或产生不可预见的结果 JAVA程序的执行过程中 如果出现异常事件,可以生成异

Java基础知识二次学习--第八章 流

第八章 流   时间:2017年4月28日11:03:07~2017年4月28日11:41:54 章节:08章_01节 视频长度:21:15 内容:IO初步 心得: 所有的流在java.io包里面 定义了多个流类型(类或抽象类)来实现输入/输出功能 可以从不同的角度对其进行分类 按数据流的方向不同可以分为输入流和输出流 按处理数据单位不同可以分为字节流和字符流 按照功能不同可以分为节点流和处理流 jdk中所提供的的所有流类型位于包Java.io内都分别继承自以下四种抽象流类型 输入流 Input

java基础知识(二)

1.关于static关键字总结: 1.不能在static修饰的方法中引用this变量,只能引用一些静态变量或方法,或new新的对象(可以定义局部变量). 简言之,静态方法或块中,只能引用静态的方法或变量. 2.类中的成员变量(static修饰)有缺省值,而类的定义的方法中的局部变量没有缺省值. 3.在类的构造器中,可以引用任何的静态或非静态的变量和方法,可以在非static方法中调用static方法. 4.static{}块中的代码在类装载中仅执行一次. 5.在7-7,A staticmetho

java 基础(二)

java 基础(二)java 基础(二) 2016-2-1 by Damon 61. 编写多线程程序有几种实现方式 Java 5以前实现多线程有两种实现方法:一种是继承Thread类:另一种是实现Runnable接口.两种方式都要通过重写run()方法来定义线程的行为,推荐使用后者,因为Java中的继承是单继承,一个类有一个父类,如果继承了Thread类就无法再继承其他类了,显然使用Runnable接口更为灵活. 补充:Java 5以后创建线程还有第三种方式:实现Callable接口,该接口中的

JAVA基础知识整理

一.首先先明白get与post的基本定义和区别: 这是两种在客户端和服务器端进行请求-响应的方法. 1get:从指定的资源请求数据. 2post:向指定的资源提交要处理的数据. get基本上用于从服务器取回数据,注意:get方法可能返回缓存数据. post可以从服务器上获取数据,不过,post方法不会缓存数据,并且常用语连同请求一起发送数据. 二. Jquery $.get()方法. $.get()方法通过Http Get发起请求,从服务器上请求数据. 语法:&.get(URL,callback