admin管理员组

文章数量:1567752

1.Java的跨平台原理:

Java通过不同的系统、不同的版本、不同的位数的Java虚拟机,来屏蔽不同的系统指令集差异,从而对外提供统一的接口(API)。 对于普通的Java开发者而言,只需要按照接口开发即可。如果我们的系统需要部署到不同的环境时,只需要在系统上安装对应版本的虚拟机即可。

##操作系统的理解:
如果想要把运行的程序应用到各种机器上,如常见的服务器、笔记本、台式电脑、手机等等,就需要为这些设备写程序,就必须和这些机器的物理接口打交道,这个过程很复杂。然而,我们开发时希望将自己的关注点在业务逻辑上,而不是设备细节上。因此,就应运而生了操作系统,它存在于设备和程序之间,这样我们的程序就只需要和操作系统打交道。(理论上是这样说的,但是操作系统仍然是一个比较底层的概念,开发的过程中仍需要和操作系统的细节打交道。)

2. 搭建一个Java开发环境的步骤:

(1)适用于我们开发环境的JDK;

(2)对应于开发环境的Eclipse;

(3)如果是web开发,还需要web服务器(常用的是tomcat);

首先,下载对应的组件;
然后,安装:

1)JDK,按照正常流程安装即可。注意需要配置环境变量JAVA_HOME,因为后面安装的Eclipse和Tomcat会依赖这个变量;

2)Eclipse,正常解压安装就可以。注意需要设置workspace的默认编码;

3)Tomcat,正常解压安装就ok。需要把tomcat集成到eclipse中,安装插件就ok;

......

4)安装svn、git等。

3.Java中的int数据占几个字节?(Java中有几种基本数据类型:8种)


数值类型 大小(二进制位数) 范围 默认值

byte(字节) 8位(1个字节) -128~127 0

short(短整型) 16位(2个字节) -32768~32768 0

int(整型) 32位(4个字节) 0

long(长整型) 64位(8个字节) 0

float(浮点型) 32位(4个字节) 0.0f

double(双精度) 64位(8个字节) 0.0f

char(字符型) 16位(2个字节)

boolean(布尔型) 1位 false


int占4个字节,32位;

4.面向对象的特征有哪些方面?

原则:回答比较抽象的问题的时候,要举例说明

四大基本特征:封装 继承 抽象 多态

1)封装性:将对象封装成一个高度自治和相对封闭的个体,对象的状态(属性)有这个对象自己的行为(方法)来读取或改变。

2)抽象性:找出事物的共性和相似之处,把现实生活中的对象,抽象成类。

3)继承性:在定义或者实现一个类的时候,可以在已经存在的类的基础上;把已经存在的类所定义的内容作为自己的内容,也可以加入若干新的内容,或者修改原来的方法是之更加适合特殊的需求,这就是继承。例如:遗产的继承

4)多态性:程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时并不确定的,而是在程序运行期间才确定的。即允许不同类型的对象对同一消息做出不同的响应。例如:UserDao userDao = new UserDaojdbcImp();用父类型变量引用子类对象
  
  编译时的多态:方法重载(overload)实现的是编译时的多态 前绑定;
  运行时的多态:方法重写(override)实现的是运行时的多态 后绑定;

重写和重载的区别:

不能根据返回类型区分

  1. 方法的重写和重载都是实现多态的方式,但是重写是运行时的多态,重载是编译时的多态;

2)重载发生在同一个类里面,同名的方法如果有不同的参数列表(类型、个数不同),则视为重载;重写发生在父类和子类之间,要注意的是重写时,父类被重写的方法和子类被重写的方法有相同的返回类型;

5.有了基本数据类型,为什么还需要包装类型?

Java是一个面向对象的语言,而基本数据类型,不具备面向对象的特性。

Java中有8种基本数据类型,每一个基本数据类型分别对应一个包装类型;例如:int—Integer, boolean—Boolean, char—character, short—String

装箱和拆箱:

1) 装箱----指把基本数据类型转换成对应的包装类型;

   Integer.valueOf(2);//手动装箱,底层源码实际上是:new Integer(2);
   Integer i=2;//自动装箱,实际上在编译时会调用Integer.valueOf(2)方法来装箱;

注意:方法valueOf(), 其底层源码设置了: 值缓存 
	[即默认情况下:将一个int值直接赋值给一个Integer对象,会调用Integer.valueOf(),如果int值在-128~127之间,包括-128和127,那么就不会new新的对象,否则就会 new Integer()新的对象]
     例如:Integer a=new Integer(3);
		  Integer b=3;
		  int c=3;
		  Integer f1=100,f2=100,f3=150,f4=150;
         那么,有:
				a==b (false, new Integer(3)没有调用valueOf,因此,不是同一个对象);
			    a==c (true,先拆箱,再进行值的比较);
				f1==f2 (true: 底层调用valueOf,值缓存,-128~127之间不会new对象);
				f3==f4 (false:不在-128~127之间,new新的对象)

2) 拆箱----指把包装类型转换为基本数据类型;

Integer i=2; 
int j=i.intValue();//手动拆箱;
int j=i;//自动拆箱,实际上底层源码在编译时调用intValue();

6.“==”和equals()方法的区别:

1)“==”:比较的是两个对象的值是否相同,如果是基本数据类型的对象,直接比较值;如果是引用数据类型的对象,比较的是它们在栈中的引用地址是否相同;

2)equals:比较的是两个对象是否长得一样,即判断两个对象的某些特征是否一样。默认情况下,都是调用Object对象下面的equals()方法(注:Object是所有类的父类)。

以String类中的equals()方法为例:
首先是判断 A==B,即判断引用地址是否相同;
然后判断是否是String类型的对象,以及长度是否相同,最后,逐个字符比较是否相同。

3)hashCode:返回的是一个数值,其目的是生成一个hash码,主要用途是在对象进行散列时作为key值输入。事实上,Object类上默认的实现是:在对象的内存地址的基础上进行特定计算返回一个hash码。

equals相同的两个对象,hashCode也一定相同;反之,不成立;

内存的简单介绍:

JVM的内存区域主要分为线程私有区域【程序计数器,本地方法栈,虚拟机栈】,和线程共享区域【堆,方法区】、直接内存。

线程私有区域:生命周期和线程相同,依赖用户线程的启动、结束而创建、销毁。

线程共享区域:随着虚拟机的启动、关闭而创建、销毁。

直接内存:不是JVM运行时数据区的一部分(它被频繁使用,在jdk1.4引入的NIO提供了基于Channel与Buffer的IO方式,它可以使用Native函数库直接分配对外内存,然后使用DirectByteBuffer对象作为这块内存的引用进行操作,这样就避免了Java堆和Native堆中来回复制数据,因此在一些场景中可以显著提高性能。)

(1)堆:存放new的对象和构造器创建的对象;
(2)栈:保存 基本数据类型的变量、对象的引用地址、函数调用的现场保存;栈空间很小,操作快;
(3)方法区:存放被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等等。运行时常量池是方法区的一部分,用于存放编译时生成的各种字面量(如直接书写的“hello”)和符号引用。

例如:String str=new String("hello");
	1.上面的语句中,“hello”作为字面量存放在方法区,变量str存放在栈中,new创建出来的字符串对象放在堆中;
	2.上面的语句,创建了两个对象,一个是方法区的“hello”,一个是用new创建放在堆上的对象;

7.String、StringBuilder、StringBuffer的区别:

在Java中提供了String、StringBuilder、StringBuffer来表示和操作字符串,字符串就是字符的集合。

1)String: 是典型的immutable类,即String是内容不可变的字符串,因为String底层源码使用了final修饰的的不可变的字符数组;也是由于其不可变性,一旦创建,类似拼接和裁剪这个字符串,都会产生很多中间对象。

2)StringBuilder和StringBuffer: 是内容可以改变的字符串,因为StringBuilder和StringBuffer的底层属性没有用final来修饰可变的字符数组。

最经典的拼接字符串:
1、String进行拼接:String c="a"+"b";//会产生很多的中间对象
2、StringBuilder和StringBuffer进行拼接:
	StringBuilder sb=new StringBuilder();
	sb.append("a").append("b");

3)StringBuilder是线程不安全的,而效率较高;StringBuffer是线程安全的,而效率较低,因为加了锁synchronized,适用于频繁修改的情况。

final、finally和finalized的区别:

1)final: 用final修饰的类、方法和变量都有不同的意义:

final修饰类:这个类不可以继承拓展;如:对String类不可以继承
final修饰方法:这个方法不可以重写;
final修饰变量:如果变量是基本数据类型,则不可以变;如果是Object类型,不可以指向其他对象,但是变量的内容可以改变;

2)finally: 是保证重点代码一定要被执行的一种机制。我们可以使用try-finally和try-catch-finally来进行类似关闭数据库连接、保证unlock锁等动作。

3)finalized: 它原本的设计目的是保证对象在被垃圾回收之前完成特定资源的回收,但是finalized现在不推荐使用,因为finalized方法的对象是一个“特殊公民”,JVM需要对它进行额外处理,它的存在拖慢了垃圾回收,导致了大量对象的堆积。

不可变对象:

特征是:可以引用传递,可以缓存;一旦建立,不能被改变;线程安全;

如何实现不可变对象:
首先,final关键字来修饰(但是这无法完全保证不可变性);
然后,从接口定义,类的实现上保证不可变性

8.Java中的集合:

Java中的集合分为:存储value(Collection接口)、存储key-value(Map接口)两种。

存储value,分为List和Set:
	List:有序的,可以重复的;
	Set:无序的,不可以重复;
(判断是否重复,是根据equals和hashCode判断,也就是如果一个对象要存储在Set中,必须重写equals和hashCode方法);

存储key-value的为Map.

9.ArrayList和LinkedList的区别:

List常用的的是ArrayList、LinkedList、Vector,它们的区别和使用场景:

1)ArrayList底层使用的是数组,LinkedList底层使用的是链表;

数组和链表的区别:
	数组:可以根据下标随机访问,查询速度快;但是插入、删除元素比较慢(原因是:数组是存储在内存中一块连续的区域中,插入或者删除某元素,后面的元素就必须向后移动,因此插入、删除速度较慢);

	链表:查询速度慢(原因是:链表查询时,必须从链头开始,一个一个的找);但是插入、删除元素效率很高(原因是:链表不要求内存是连续的,且当前元素中存放下一个或者上一个元素的地址,插入、删除时不需要移动元素,只需改变指针的指向就可以);

2)ArrayList应用于随机查询多,插入、删除少的场景;LinkedList应用于随机查询少,插入、删除多的场景;

3)ArrayList和LinkedList都是线性不安全的,Vector是线性安全的(数组);

简介HashSet、TreeSet:

Set常用的是HashSet和TreeSet,它们的介绍如下:

1)HashSet(hash表): 哈希表里面存放的是哈希值,HashSet存储元素的顺序并不是按照存入时的顺序,而是 首先判断两个元素的哈希值,如果哈希值不同,则存放在不同的hashCode位置上(横向排列);如果哈希值相同,则存放在同一个hashCode位置上,然后判断equals() 的结果,如果为true,则表明这是同一个元素,如果为false,则在同一个hashCode位置上纵向排列;

2)TreeSet(二叉树):TreeSet使用二叉树原理来对新add()的对象按照指定的顺序排序(升序、降序),每增加一个对象都进行排序,将对象插入到二叉树的指定位置;

3)LinkedHashSet:继承了HashSet接口,底层使用LinkedHashMap来保存所有元素;

10.HashTable、HashMap、TreeMap的区别:

相同点:HashMap和HashTable都可以用来存储key-value的数据;

	HashTable和HashMap区别:
	(1)HashMap是可以把null作为key或者value的,而HashTable是不可以的;
	(2)HashMap是线程不安全的,效率较高;而HashTable是线程安全的,效率较低。

1)HashMap:大方向上,HashMap里面是一个数组,然后数组中的每个元素是单向链表;Java8对HashMap进行了一些修改,最大的不同是利用红黑树,即由“数组+链表+红黑树”组成。【查找的时候,根据hash值可以快速定位到数组的具体下标,之后,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为O(n);在Java中,当链表中的元素超过了8个以后,会将链表转换为红黑树,查找时可以降低时间复杂度为O(log n)】。

2)ConcurrentHashMap: 与HashMap的思路差不多,但是它支持并发操作;整个ConcurrentHashMap由一个个Segment(即分段锁)组成,每个Segment内部其实就是HashMap (默认情况下,有16个Segment,支持16个线程并写,初始化后,它是不可以扩容的,但是segment内部是可以扩容的); 简单理解,ConcurrentHashMap是一个Segment数组,每次需要加锁的操作,锁住的是一个Segment,这样只要保证每个Segment是线程安全的,才能实现全局的线程安全;

	如果想要HashMap满足线程安全,可以:
	(1)用Collections工具类的synchronizedMap方法,使HashMap具有线程安全的能力;
	(2)使用ConcurrentHashMap;

3)TreeMap:如果使用排序的映射,建议使用TreeMap。TreeMap是基于红黑树的一种提供顺序访问的Map,使用时,key必须实现Comparable接口.

11.实现一个拷贝文件的工具类使用字节流还是字符流?

我们拷贝的文件不确定只包含字符流,有可能有字节流(例如:图片、声音、图像等),为了考虑到通用性,要使用字节流。

12.简介线程:

1)线程的几种实现方式:

(1)通过继承(extends)Thread类实现一个线程;
(2)通过实现(implement)Runnable接口实现一个线程;
(3)JDK 1.5之后,通过实现Callable接口实现一个线程;

2)怎么启动线程?

启动线程调用的是start()方法,而启动之后执行的是run()方法;
具体如下:
	Thread thread = new Thread(继承了Thread类的对象、或者实现了Runnable接口的对象);
	thread.start();

3)怎么区分线程?(如果在一个系统中有很多线程,每个线程都会打印日志,我想区分是哪个线程打印的怎么办?)

这是一种规范,在新建了一个线程之后,都需要设置名称:
具体如下:
	Thread thread = new Thread(new MyThread());
	thread.setName("设置一个线程名称");

4)线程的五个状态:

(1)新建状态[new]:new Thread();

(2)就绪状态[runnable]:一个新建的线程不会自动开始运行,必须调用线程的start()方法,当start()方法返回时,线程就处于就绪状态,这意味着:线程所处的虚拟机处于可运行状态,而不意味着线程会立即运行;

(3)运行状态[running]:处于就绪状态的线程不一定立即运行run()方法,线程还必须和其他线程竞争CPU时间,只有获得CPU的时间,才能执行run()方法,进入运行状态;

	(注:单个CPU计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态,可能多个线程处于就绪状态,对于多个处于就绪状态的线程,它们由Java系统的线程调度程序来调度。)

(4)阻塞状态[blocked]:

	线程的运行过程中,可能由于各种原因进行阻塞状态:
	(1)线程通过sleep()方法进入睡眠状态;
	(2)线程通过wait()方法进入等待池;
	(3)线程调用一个I/O上被阻塞的操作,即该操作在输入、输出操作完成之前,不会返回给它的调用者;
	(4)线程试图得到一个锁,而该锁正在被其他线程持有;
	(5)线程在等待某个触发条件;
	(6)执行同步方法或者同步代码块进入了等锁池;
	 ... ...

(5)死亡状态[dead]:

		有两个原因会导致线程死亡:
		(1)run()方法正常退出,而自然死亡;
		(2)一个未捕获的异常终止了run()方法,而使线程猝死;

	可以用isAlive()方法来判断线程在当前是否存活:如果线程是可运行的、或者被阻塞的,则方法返回true;如果线程是new状态且是不可运行的、或者线程死亡了,则返回false。

5)sleep()、wait()、yield()的区别和联系:

相同点:这些方法都可以让线程暂停执行;

sleep()方法和wait()方法的区别:
(1)sleep()方法是Thread类的静态方法,调用此方法,使得当前线程暂停执行,将CPU让给其他线程,当前线程进入阻塞状态;但是对象的锁依然保持着,因此,当前线程在休眠结束后,会自动恢复、回到就绪状态;
(2)wait()方法是Object类的方法,调用对象的wait()方法,导致当前线程暂停执行,并且放弃对象的锁,当前线程进入阻塞状态(即进入对象的等待状态);当调用notify()方法后,该线程会才会会被唤醒,进入等锁池;如果线程重新获得对象的锁,则该线程进入就绪状态;

sleep()方法和yield()方法的区别:
(1)sleep()方法给其他线程让出CPU的时候,不会考虑其他线程的优先级;但是yield()方法只会给相同或者更高优先级的线程让出CPU;
(2)线程调用sleep()方法之后进入阻塞状态;但是线程调用yield()方法之后直接进入就绪状态;
(3)sleep()方法声明抛出InterruptedException;但是yield()方法没有声明任何异常。

5)当一个线程进入一个对象的synchronized方法A之后,另一个线程是不能进入该对象的synchronized方法B的,为什么?

因为:其他线程只能访问该对象的非同步方法,同步方法则不能进;非静态方法上的synchronized修饰符,要求执行方法时,要获得对象的锁,如果已经进入方法A,说明对象所已经被取走了;那么试图进入方法B的线程就只能在等锁池中等待对象的锁(注意:不是等待池)。

6)多线程中要保证线程安全,就必须加锁,但是可能出现“死锁”现象;

死锁条件,必须同时满足:
条件一:互斥等待;(一个人抢到了锁,没有抢到锁的人必须等待第一个人做完)
条件二:hold and wait;(即拿着一个锁,去等待另一个锁)
条件三:循环等待;(比如:一个人拿着a的锁去等待b,而另一个人拿了b的锁去等待a)
条件四:无法剥夺的等待;(有些wait是有时间限制的,超时后会自动解锁)

7)线程并发库:(有没有使用过线程并发库?答:简单了解过)

JDK 1.5 增加了DougLea的并发库,引入了java.util.current包;

Java通过 Executors类 提供了4个静态方法,去创建4种线程池,分别为:
(1)new CachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可以灵活回收空闲线程,若无可回收,则新建线程;
(2)new FixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待;
(3)new ScheduledThreadPool创建一个定时线程池,支持定时及周期性任务执行;
(4)new SingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。


线程池的作用:(连接池也是一样的)
(1)线程池不用每次都去创建,响应时间更快;
(2)线程池不需要每次都去创建或者销毁,节约了资源;
(3)限定线程的个数,不会使得由于线程过多而导致系统运行缓慢或者崩溃;

13.简单了解设计模式

1)什么是设计模式?

设计模式就是经过前人无数次的时间总结出来的,设计过程中可以反复使用的、可以解决特定问题的设计方法

2)常用的设计模式有哪些?

(1)单例模式(包括:饱汉模式、饿汉模式)
	三部曲:(一)、构造方法私有化,让除了自己类中能创建外、其他地方都不能创建;
		   (二)、在自己的类中创建一个单实例;(饱汉模式是一出来就创建单实例,而饿汉模式是需要的时候才创建)
		   (三)、提供一个方法获取该实例对象;(饿汉模式在创建时需要进行方法同步)
	具体写一个单例模式:
	(一)、饱汉模式
		public class PersonFactory{
			//构造方法私有化
			private PersonFactory(){
			}
			//创建一个单实例
			private static PersonFactory instance = new PersonFactory();
			//提供方法获取该实例对象
			public static PersonFactory getInstance(){
				return instance;
			}
			//使用的时候
			public static void main(String[] args){
				PersonFactory.getInstance().某个方法;
			}
		}
	(二)、饿汉模式
		public class PersonFactory{
			//构造方法私有化
			private PersonFactory(){
			}
			//创建一个单实例
			private static PersonFactory instance = null;
			//提供方法获取该实例对象
			public synchronized static PersonFactory getInstance(){
				if(instance == null){
					instance = new PersonFactory();
				}
				return instance;
			}
			//使用的时候
			public static void main(String[] args){
				PersonFactory.getInstance().某个方法;
			}
		}
(2)工厂模式:Spring IOC 就是使用工厂模式;
(3)代理模式:Spring AOP 就是使用代理模式;
(4)包装模式:

本文标签: 小结基础知识Java