1、 内部类
静态内部类:相当于又添加了一层命名空间限制,与外部类相互独立。
成员内部类:属于外部类某一个具体对象、包含外部类对象的隐含引用。
局部内部类:属于外部类某一个具体对象、包含外部类对象的隐含引用。
匿名内部类:属于外部类某一个具体对象、包含外部类对象的隐含引用。
2、 Java中的访问控制
3、 Java中的字面量默认值
整型值:int
浮点型:double
Java中任何可能会导致精度丢失的隐式转换都会在编译时报错。
4、 Java中的高精度计算
BigInteger、BigDecimal
5、 Java中8中基本数据类型
byte/short/int/long/float/double/char/boolean
Byte/Short/Integer/Long/Float/Double/Character/Boolean
6、 字符串字面量
(1)、JVM在遇到用双引号括起来的字符串时,就会自动去String常量池检查是否存在该对象,如果不存在则创建一个新对象放到常量池中。
(2)、运行时常量池是内存中的一个单独区域,处于方法区中。而不是堆区、或者栈区。
(3)、注意如果用new String(“abc”)显示创建String对象时,此时创建的对象会是直接在堆区中,而不再是指向常量池中的对象。
7、 Java中内存区域划分
(1)、程序计数器:是当前线程所执行的字节码的行号指示器、每条线程都需要一个独立的程序计数器、所以程序计数器是“线程私有”的内存、程序计数器区域是Java虚拟机中唯一没有定义OutOfMemory异常的区域。
(2)、Java虚拟机栈:和程序计数器一样也是线程私有的,生命周期与线程相同、是Java方法执行的内存模型:每个Java方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。
(3)、本地方法栈:本地方法栈(C/C++方法)和虚拟机栈基本类似,只不过Java虚拟机栈执行的是Java代码(字节码),本地方法栈中执行的是本地方法的服务。本地方法栈中也会抛出StackOverflowError和OutOfMemory异常。
(4)、Java堆:堆是Java虚拟机所管理的内存中最大的一块。堆是所有线程共享的一块区域,在虚拟机启动时创建。堆的唯一目的是存放对象实例、Java堆是垃圾收集器管理的主要区域、Java堆可以是物理上不连续的空间,只要逻辑上连续即可,主流的虚拟机都是按照可扩展的方式来实现的。
(6)、方法区:方法区也是线程共享的区域,用于存储已经被虚拟机加载的类信息,常量,静态变量和即时编译器(JIT)编译后的代码等数据。
(7)、运行时常量池:运行时常量池是方法区的一部分,Class文件中除了有类的版本,字段,方法,接口等信息以外,还有一项信息是常量池用于存储编译器生成的各种字面量和符号引用,这部分信息将在类加载后存放到方法区的运行时常量池中。
8、 常量池
Java 5开始,就对八种基本数据类型加上String类型都建立了常量对象池。
9、 StringBuilder和StringBuffer
StringBuilder是线程安全
10、 字符串反转
利用StringBuilder或者StringBuffer的reverse方法。
11、 Java中指定字符集创建字符串
Stringstr1 = "abc";
String encodeStr = new String(str1.getBytes(),"GBK");
注:str1.getBytes()使用的是平台默认的编码方式转换成字节数组,如果Str1的编码方式和平台默认的编码方式不同例如UTF-8,则需使用str1.getBytes("UTF-8")
12、 java 中常见类型数组的类名
int[]arr1 = new int[10]; //[I
double[]arr2 = new double[10];//[D
float[]arr3 = new float[10];//[F
char[]arr4 = new char[10];//[C
String[]arr5 = new String[10];//[Ljava.lang.String;
boolean[]arr6 = new boolean[10];//[Z
short[]arr7 = new short[10];//[S
long[]arr8 = new long[10];//[J
byte[]arr9 = new byte[10];//[B
ClassA[] arr10 = new ClassA[10];//[LclassTest.ClassA;
13、 Java中数组的创建与C++中的区别
(1) Java中用new来创建一个基本数据类型的数组时,会为该数组分配指定长度的内存空间,并且把每个元素的值按照默认值进行初始化。
(2) Java中用new来创建一个引用类型的数组时,Java编译器只会分配指定长度的引用变量的内存空间,而该数组中的每个具体元素都是null。包括基本数据类型的包装类型。(这一点与C++中不同,C++也会直接为对象开辟空间并调用构造函数初始化。)
14、 java中数组的拷贝
(1) Java中数组也是一个引用类型对象,任何引用类型对象通过=进行赋值时,都只是复制了引用,没有事先真正的深拷贝。
(2) Java中的数组对象的拷贝方法有:1、数组对象的.clone()方法。2、利用System.arraycopy()方法。
15、 Java和C++中多维数组的维度和每维的长度都是不固定的,都是先声明高维长度
int[][] arr =new int[3][];//JAVA写法
int a[2][3];//C++
int * a = new int*[3];//C++
16、 java.util.List;与java.awt.List;的区别
(1)java.util.List是一个表示数据集合的接口,而java.awt.List是一个提供可滚动文本项列表的组件,只能存储String类型数据。
(2)java.util.*是和图形绘制相关的类,如Point、Line等。
17、 Java中的foreach是直接用for语句实现的
for (Integer integer : list){}
18、 Java中Map本身是没有没有继承Iterator接口,但Map的成员函数entrySet()能返回一个Entry
19、 各种编码格式区别
(1) Unicode只是一种符号映射集合,它只规定了每个符号所对应的二进制代码,却没有规定这个二进制代码该如何存储,即:没有规定以总长多少个字节存储。
(2) UTF-8和UTF-16、ISO8859-1、GBK等都是Unicode编码的一种具体实现,规定该编码的存储格式。所以在存储到文件时一定是一个具体的编码方式而不是Unicode。
(3) 程序中采用Unicode编码,只是能够按照这种字符集把字符转换成对于的二进制和把二进制解读成对于字符。
(4) “Unicode字符是2个字节”这句话是有问题的,Unicode并没有规定每个字符的二进制表示长度该是多少个字节,只有具体编码方式才能知道。主要是因为windows默认的unicode编码就是UTF-16。而unicode在这种编码下,大部分都是2个字节的,至少上面提到的前65536个基本常用字符都是2个字节的。所以就有了“unicode字符是2个字节”这句话。
20、 java中的集合框架
集合框架图如下:
21、 Java中比较器
(1) Comparable接口:如果一个实体类实现了该接口,则该类时可比较的,同时包含该类对象的集合collection就能直接调用自身的sort(null)方法或者Collections.sort(list)、Arrays.sort(list)对该集合或数组进行排序。
(2) Comparator接口:实现该接口的对象称为比较器,可以将该对象作为参数传递给集合的sort方法,强制该集合或数组按照该比较器的方法进行排序。
22、 Java中Vector、ArrayList、LinkedList的区别
首先,三者都实现了接口List(线性表),都是一种线性表容器。
(1) Vector:(顺序表)基于动态对象数组来存储元素,vector是线性安全的,所以效率没有ArrayList高。
(2) ArrayList:(顺序表)基于动态对象数组来存储元素,与Vector功能用法一样,但是不是线性安全的,同时也因此效率比Vector高。
(3) LinkedList:(链表)基于链表的数据结构,删除、插入的效率比顺序表要高,但是查询的效率比顺序表要高很多。
23、 Java中HashMap、HashTable、TreeMap的区别
首先,都实现了Map接口,都是一种键值对映射集合。
(1) HashTable是线程安全的,而HashMap是线程不安全的,不能同步。多线程的场合要使用HashTable。
(2) HashTable中不允许出现null(key和value都不允许),而HashMap中key和value都可以出现null。
(3) 遍历方法不一样,HashTable除了可以Iterator遍历外还可以用Enumeration遍历,而HashMap只能用Iterator遍历。
TreeMap和HashMap比较:
(1) 两者都是非线性安全的
(2) HashMap是基于哈希表实现的,使用时HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()],为了优化HashMap空间的使用,您可以调优初始容量和负载因子。
(3) TreeMap:基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。
(4) HashMap中的元素是无序的,而TreeMap中的元素是默认按照key值进行排序的,同时也可以在构建TreeMap对象时传入比较器,使TreeMap按特定顺序排序。
(5) HashMap的效率比TreeMap高。如果不要排序Map则使用HashMap。
24、 Java中Enumeration接口与Iterator接口的区别
Enumeration和Iterator都是用来对集合进行遍历的,但是区别如下:
(1) Enumeration只能适用于Vector和HashTable集合遍历,而iterator是Enumeration的替代品,可以遍历大多数的集合融合。
(2) Enumeration只能遍历集合元素,而Iterator除了能遍历元素外还能删除集合中的元素。
(3) 注意Iterator中的remove()表示把刚刚next出来的元素删除掉。
25、 Java中HashSet和TreeSet区别
(1) HashSet:是基于哈希表结构实现的,HashSet中的数据是无序的,元素可以为null,但只能有一个为null。
(2) TreeSet:是基于红黑树结构实现的,元素不允许为null,TreeSet可以确保集合中的元素处于排序状态,排序的方式有两种:自然排序和定制排序。自然排序按照元素自动的compareTo方法进行排序;定制排序:在创建TreeSet对象时传入一个特定的比较器即可。
26、 Java中泛型定义与类型
(1) Java中泛型就是C++中的模板,主要功能是类型参数化。
(2) Java中之所以出现泛型T而不用object类型代替,主要是object能用多种类型的对象来赋值,而使用泛型时我们确定了类型参数后就只能使用固定的类型对象来赋值。
(3) Java中的泛型使用范围和C++一样,都有泛型类(模板类)、泛型接口、泛型方法。
(4) Java中泛型方法直接将泛型参数列表置于函数返回值类型之前即可,且与拥有泛型方法的类是否是泛型没有任何关系。
(5) Java中的static成员方法,由于无法获取到类的泛型参数,所以如果static方法需要有泛型能力,则必须使用泛型方法。
27、 Java中泛型实现原理与C++模板的区别
(1) C++中的泛型实现方法:编译时具体化。也就是C++程序在编译时将泛型中的参数T直接用调用该对象或方法时传入的参数类型具体化,所以用不同类型参数初始化的泛型类对象是的类型是不相同的,内存中也存在该类的多个不同版本。
(2) Java中泛型的实现方法:编译时擦除法。也就是Java程序在编译时直接将泛型类型T擦除成指定的边界类型(例如
(3) Java中泛型擦除法的原因:主要是因为泛型不是在java1.0版本中提出来的,所以为了向后兼容旧的非泛型代码使用了擦除法,但是擦除是一定会导致性能下降的。
(4) Java中泛型擦除法的缺点:1、所有具体化的泛型类都是同一类型,通过getClass()方法获得的类型区别不了之间的区别。2、泛型类、接口、方法在编译时泛型参数会被擦除成边界类型处理,也就是说代码在编译时编译器是不知道T的具体类型的,所以在泛型类、接口、方法中不能使用instanceof操作来判断T的类型和使用new表达式来创建一个泛型类型对象。(因为instanceof和new操作都是在编译器进行的操作)
28、 Java中集合元素排序、
(1) 如果该集合有排序方式sort例如List,则可以直接用该方法进行排序,但注意需要排序的元素类型本身实现了comparable或者给Sort方法传递一个实现comparator接口的比较器。
(2) 如果该集合没有排序功能,则可以使用Collections.Sort()方法进行排序,同样,需要序的元素类型本身实现了comparable或者给Sort方法传递一个实现comparator接口的比较器。
29、 Java中的foreach语句
(1) Java中的foreach语句的表达式还是用for关键字,for(Type t:list);
(2) Java 中foreach语句的使用于数组或者实现了Interable接口的类实例。
30、 Java中操作文件和目录
(1) Java中操作文件和目录都是使用File类型,常用的方法有exists();isDirectory();isFile();createNewFile();mkdir();mkdirs();
(2) 使用createNewFile()文件创建的时候不加.txt或者其他后缀也是文件,不是文件夹;使用mkdir()创建文件夹的时候,如果起的名字是比如aaa.txt也是文件夹不是文件。
(3) Java中的删除不走回收站,且要删除一个文件夹时,请注意该文件夹内不能包含文件或者文件夹,否则将会删除失败。
(4) String[] list():获取指定目录下的所有文件或者文件夹的名称数组。
(5) File[] listFiles():获取指定目录下的所有文件或者文件夹的File数组。
31、 文本文件各格式文件头
ANSI类型:什么都没有
UTF-8类型(三个字节):EF BB BF
UNICODE类型:FF FE
UNICODE BIG ENDIAN类型:FE FF
32、 Java中FileInputStream输入字节流中的read()方法为什么返回的是一个int类型,而不是byte类型?
主要元素是read方法在判断文件结束时返回-1,所以程序中通常是检测read()方法的返回值,如果为-1则结束读取循环,所以如果读取的文件中遇到一个字节的内容为11111111时,由于11111111(计算机中是补码表示负数)的转换为byte类型正好为-1,从而会与文件结束时返回的标识-1冲突。所以read()方法采用返回一个int值,文件中11111111字节转换为int类型为255,不会与结束时返回的-1冲突。
33、 Java中IO流框架
34、 Java中BufferedInputStream和BufferOutputStream作用?
(1) BufferedInputStream内置了了一个缓冲区(数组),从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性先从文件中读取8192个字节缓冲到缓冲区,并返回给程序一个字节,程序再次读取时,就不用找文件了。
(2) BufferOutputStream也内置了一个缓冲区(数组),程序向流中写出字节时,不会直接写到文件,而是先写到缓冲区中,直到缓冲区写满,BufferOutputStream才会把缓冲区中的数据一次性写到文件里。
35、 什么是字符流?
(1) 字符流是可以直接读写字符的IO流、
(2) 字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出。
36、 什么情况下使用字符流?
(1) 字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节。
(2) 程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流。
(3) 读取的时候是按照字符的大小读取的,不会出现半个中文。
(4) 写出的时候可以直接将字符串写出,不用转换为字节数组。
37、 字符流是否可以拷贝非纯文本文件?
不可以拷贝非纯文本的文件,因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去。如果是?,直接写出,这样写出之后的文件就乱了,看不了了。
38、 Java中如何进行随机存取文件?
RandomAccessFile类提供了随机存取文件的功能,该类像操作一块内存区域一样操作文件,把字节用下标数字来进行定位。
常用的方法有:
(1) length()获取文件的内容长度。
(2) seek(index)随机移动文件指针到任何需要存取数据的地方。
(3) 用read()方法获取当前位置的数据。
(4) 用write()写数据。注意会把当前文件指针指向的字节覆盖。
39、 Java中InputStreamReader和InputStreamWriter
Java中对文件文件进行读写时,为了避免出现因为格式问题乱码,最好使用InputStreamReader和InputStreamWriter在创建流时就指定流解析和编码字符的格式,而不是通过先通过按字节进行读取或者不指定编码先按字符进行去读,再对字符串进行编码。因为这样不指定编码时字符流的解析会先按照系统默认的编码格式解析,如果文件编码格式与系统默认编码格式不同的话就很可能会产生乱码。
40、 Java中如何实现序列化
(1) 让需要序列化的类实现Serializable接口,同时也需要让该类提供静态的long型常量serialVersionUID。
(2) 如果是序列化对象,则用一个输出流创建一个ObjectOutputStream对象,然后调用writeObject方法。
(3) 如果是饭序列化,首先使用一个输入流创建一个ObjectInputStream对象,然后调用readObject()方法,得到一个Object类型的对象。最后再做类型的强制转换。
41、 Java序列化中serialVersionUID字段的作用
(1) SerialVersionUid的目的是序列化对象版本控制,有关各版本反序列化时是否兼容。如果在新版本中这个值修改了,新版本就不兼容旧版本,这时旧版本的对象文件反序列化时会抛出InvalidClassException异常。如果类的修改不大,比如仅仅是增加了一个属性,我们希望向下兼容,老版本的数据都能保留,那就不用修改;如果我们删除了一个属性,或者更改了类的继承关系,必然不兼容旧数据,这时就应该手动更新版本号,即SerialVersionUid。
(2) 如果不显式设置SerialVersionUid,有什么后果?
jdk文档中有解释,建议我们显式声明,因为如果不声明,JVM会为我们自动产生一个值,但这个值和编译器的实现相关,并不稳定,这样就可能在不同JVM环境下出现反序列化时报InvalidClassException异常。
42、 Java中建立多线程的方法
(1) 方法一:建立一个实现Runnable接口的线程类,并且用该类对象作为Thread类的构造函数的参数,创建一个Thread对象,最后调用Thread对象的start方法开启一个线程。
(2) 方法二:建立一个集成自Thread类的线程类,创建该线程类的对象,最后执行该对象的start方法启动一个线程。
43、 Java多线程中Thread类和Runnable接口的区别?
(1) Thread类本身就实现了Runnable接口,所以本质上线程类是需要实现Runnable接口的,只是Thread类还提供了一些额外的方法。
(2) 线程类继承自Thread则不能继承自其它类,而实现Runnable接口可以。
(3) 继承自Thread类的线程类提供了更多关于线程的方法,如:获取线程的id、线程名、线程状态、以及对线程进行休眠、阻塞、终止等方法。
44、 Java中的线程同步
这里所指的线程同步主要指:Java多线程之间安全并发,同步执行而不会发生像死锁这样的安全问题。Java中的线程同步主要是通过sychronized关键字来实现的,sychronized关键字的原理是利用任何一个对象的对象锁来锁住一段代码,任何想要进入该代码的线程必须先获得该对象锁才能继续执行,否则就会进入等待状态,且只有等占有该对象锁的线程执行完毕以后,该对象锁才会被释放。具体的用法如下:
(1) sychronized加在一段代码上,叫做同步代码块,则需要为该关键字提供一个对象的引用。
(2) sychronized加在某一个成员方法上,叫做同步方法,此时的锁是加在this说引用的对象上的。
45、 Java中Object的方法wait()、notify()、notifyAll()方法的作用?
Java中的wait、notify、notifyAll方法是Object类的final native方法,这些方法不能被子类重写。这三个方法的主要作用是控制Java多线程之间的协同工作,即制约线程之间的执行进度。
(1) Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁的对象进行操作。所以wait、notify、notifyAll必须是放在synchronized(Obj)的范围内执行,否则会报出IllegalMonitorStateException异常。
(2) Obj.wait():线程在获得该Obj对象锁之后,主动释放该对象锁,同时休眠本线程,直到其他线程调用该对象的notify唤醒该线程,才能继续获取对象锁,并继续执行。
(3) Obj.notify():在获得该Obj对象锁之后,首先唤醒因为该Obj对象wait而休眠的线程,并且在相应的synchronized(){}语句结束后释放该对象锁。
(4) 注:Obj.notify()在是在synchronized语句块结束后释放自己拥有的Obj对象锁,而Obj.wait()是在休眠本进程的同时就释放了对象锁。
(5) Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。
46、 Java中的线程池
线程池属于对象池,主要也是为了最大程度的对线程进行复用,主要包括以下几个部分:
(1) 完成任务的一个或多个线程
(2) 用于调度管理的管理线程
(3) 要求执行的任务队列
47、 Java中线程池Executor框架
Executor 执行器接口:该接口定义执行Runnable任务的方式。
ExecutorService: 该接口定义提供对Executor的服务。
ScheduledExecutorService:定时调度接口。
AbstractExecutorService:执行框架抽象类。
ThreadPoolExecutor: JDK中线程池的具体实现。
Executors: 线程池工厂类。
以上中常用的线程池类主要是ThreadPoolExecutor和Executors。
方法一:ThreadPoolExecutor 线程池类创建线程池
JDK中的线程池均由ThreadPoolExecutor类实现。其构造方法如下:
ThreadPoolExecutor(intcorePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnitunit,BlockingQueue
参数说明:
corePoolSize:核心线程数。
maximumPoolSize:最大线程数。
keepAliveTime:线程存活时间。当线程数大于core数,那么超过该时间的线程将会被终结。
unit:keepAliveTime的单位。java.util.concurrent.TimeUnit类存在静态静态属性: NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS
workQueue:Runnable的阻塞队列。若线程池已经被占满,则该队列用于存放无法再放入线程池中的Runnable。
handler:表示当拒绝处理任务时的策略。
任务提交给线程池之后的处理策略,这里总结一下主要有4点:
· 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
· 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
· 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
· 如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
方法二:使用Executors工厂方法创建线程池
JDK内部提供了五种最常见的线程池。由Executors类的五个静态工厂方法创建。
· newFixedThreadPool(...)
· newSingleThreadExecutor(...)
· newCachedThreadPool(...)
· newScheduledThreadPool(...)
· newSingleThreadScheduledExecutor()
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
48、 Java中反射的原理
Java中的反射主要是通过Class类来实现的,JVM虚拟机在每加载一个类到内存中时,就会自动为该类创建一个Class类的实例,该Class类对象包含了被加载类的全部信息,包括类名、属性、方法、构造器、修饰符等信息。开发者在通过改Class实例获取这些信息后,就可以利用该Class对象创建实例、调用任何方法、访问任何属性。
类在什么时候会被加载到JVM中?
(1) 使用该类显示的创建对象。
(2) 访问该类的静态成员。
(3) 使用Class类的静态方法forName()方法,动态的加载一个指定类名的类。
怎样才能获取一个类的Class对象?
(1) 使用Class类的静态方法forName()方法,动态的加载一个指定类名的类时,该方法的返回值就是一个Class类对象。
(2) 访问所有类都会拥有的静态的class属性。
(3) 调用所有对象都会拥有的getClass()方法。
怎样利用反射访问一个类的成员变量?
(1) 首先,获取到该类的Class对象,然后调用Class对象的getDeclaredField(“字段名”)或者getField(“字段名”)方法获得该成员字段的Field对象。注意:getDeclaredField既能获取到public属性也能获取到private属性的Field对象,而getField方法只能获取到public类型的属性的Field对象。
(2) 接着利用Filed对象的getXXX()和setXXX()方法就可以直接获取或者设置该字段的值了,注意:如果需要访问或设置的值是private类型时,需要先用setAccessible(true)设置该私有字段的访问权限为true时才能用使用getXXX()和setXXX()方法,否则将会报错。
怎样利用反射访问一个类的成员方法?
(1) 首先,获取到该类的Class对象,然后调用Class对象的getDeclaredMethod (name, parameterTypes)或者getMethod (name, parameterTypes)方法获得该成员方法的Method对象。注意:getDeclaredMethod既能获取到public方法也能获取到private方法的Method对象,而getMethod方法只能获取到public类型方法的Method对象。
(2) 接着利用Method对象的invoke(obj, args)方法就可以直接调用该方法,obj表示该类的对象,args表示该方法的参数列表。注意:如果需要调用的成员方法是private时,则必须在invoke方法前,先使用Method的setAccessible(true)设置该对象的访问权限为true。否则invoke会IllegalAccessException异常。
怎样利用反射实例化一个类?
(1) 首先,加载该类到JVM内存中,并获得该类的Class实例。
Class class2 = Class.forName("classTest.ClassB");注意:forName中类名一定要包名路径的全称。
(2) 使用无参构造函数创建对象
Object obj = class2.newInstance();
(3) 使用有参构造函数创建对象,先通过Class对象获取到该类的构造函数Constructor对象,然后再利用Constructor对象创建实例。
Constructor constructor = class2.getConstructor(int.class,int.class);
Object obj2 = constructor.newInstance(2,2);
49、 Java中的TCP编程过程
服务器端:
(1) 创建ServerSocket对象,并指定端口号。
(2) 开始调用ServerSocket的accept()方法开始对端口进行监听,并获得建立链接之后的Socket对象。
(3) 利用Socket的getOutputStream和getInputStream方法获得输入输出流,进行数据传输。
(4) 释放资源,关闭输入输出流、socket对象和ServerSocket对象。
客户端:
(1) 客户端只需要创建Socket对象,在构造函数中传入服务器端对应的ip地址和端口号。
(2) 利用Socket的getOutputStream和getInputStream方法获得输入输出流,进行数据传输。
(3) 释放资源,关闭输入输出流、socket对象。
50、 Java中UDP编程过程
(1) UDP协议通信的服务器端和客户端编程是一样的,都使用DatagramSocket类,并没有什么区别。
(2) 与TCP不同的是UDP通信的两端都有自己的端口号,且两个端口号不必相同。
(3) 接收消息的一端和发生消息的一端,首先分别建立自己的DatagramSocket对象,并设置自己的端口号。
(4) 然后再建立自己的DatagramPacket对象,区别是发送端在创建DatagramPacket需要指明接收数据的端IP地址和端口号。
(5) 接收端调用DatagramSocket的receive(packet)方法和getData方法接收数据。
(6) 发生端调用DatagramSocket的send(packet)方法发送数据。
// 接收端接收端端的端口号9998
DatagramSocket datagramSocket = null;
try {
//创建数据报Socket对象
datagramSocket = new DatagramSocket(9998);
byte[] bytes = new byte[256];
DatagramPacket packet = new DatagramPacket(bytes, 256);
datagramSocket.receive(packet);
//以下两种方法都能获得接收到的数据
System.out.println(new String(packet.getData()));
System.out.println(new String(bytes,0,packet.getLength()));
} catch (Exception e) {
e.printStackTrace();
}finally {
datagramSocket.close();
}
//UDP测试 接收端接收端的端口号9997
DatagramSocket datagramSocket = null;
try {
datagramSocket = new DatagramSocket(9997);
String string = "hello1";
//在数据报中指定接收端的ip地址和端口号
DatagramPacket packet = new DatagramPacket(string.getBytes(), 0,string.getBytes().length,InetAddress.getByName("localhost"),9998);
//发生报文
datagramSocket.send(packet);
} catch (Exception e) {
e.printStackTrace();
}finally {
datagramSocket.close();
}
51、 Java中如何访问web站点?
(1) 用URL类创建一个资源定位符URL的对象。
(2) 调用该URL对象的openConnection()方法得到HttpURLConnection对象。
(3) 调用HttpURLConnection对象的open()方法打开连接。
(4) 调用getHeaderFields()方法得到相应结果的头部信息。
(5) 用getInputStream()方法得到输入流对象,得到响应内容。
(6) 用disconnect()方法关闭连接。
52、 如何使用jdbc事务?
(1) 加载数据库驱动器,创建connnection连接。
(2) 调用connnection对象的setAutoCommit(false)方法关闭自动提交。
(3) 创建statement,执行SQl语句。
(4) 调用connnection对象的commit()方法手动提交事务。
(5) 对以上代码进行try catch异常处理,并在catch语句块中进行回滚操作。
(6) 关闭连接。