Java基础学习与面试(1)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
1、stringBuffer与StringBuilder的区别
(1)在对字符串进行修改的时候使用,与string不同的是,stringBuffer与stringBuilder类的对象能够被多次修改,并不产生新的未使用对象;
(2)二者之间最大的区别是stringBuffer采用synchronized(同步锁)进行修饰,因此是线程安全的,而stringBuilder是线程不安全的;
(3)在单线程程序下,stringBuilder效率更高,因此多数情况下多使用stringBuilder类;
(4)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(5)单线程操作字符串缓冲区下操作大量数据 StringBuilder;

2、如何让两个线程交替执行?
知识点:涉及同步锁问题(synchronized与lock)
(1)wait()、notify/notifyAll()是本地final方法,无法被重写;
(2)wait()使当前线程阻塞,(前提是必须先获得锁,一般配合synchronized 关键字使用)即,一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法,所以说明当前线程一定是获取了锁。
(3)当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。也就是说,notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程让其获得锁。
(4)wait() 需要被try catch包围,以便发生异常中断也可以使wait等待的线程唤醒。
例如:

public class K {
    //状态锁
    private Object lock;
    //条件变量
    private int now,need;
    public void produce(int num){
        //同步
        synchronized (lock){
           //当前有的不满足需要,进行等待,直到满足条件
            while(now < need){
                try {
                    //等待阻塞
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我被唤醒了!");
            }
           // 做其他的事情
        }
    }
}

两个线程交替执行程序:
(1)采用公平锁方法(即可重入锁)
A、ReentrantLock与synchronized一样,都是独占锁,不过ReentrantLock的加锁与解锁的过程需要手动进行,位置灵活,而synchronized加锁和解锁的过程自动进行,易于操作。
B、ReentrantLock与synchronized都可重入,synchronized不必担心最后是否释放锁;而ReentrantLock因加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
C、synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。
D、公平锁:在锁上等待时间最长的线程将获得锁的使用权,即谁排队时间最长谁先执行获取锁。

package com.thread.synchronizedDemo.lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockDemo implements Runnable {
	
	// 定义一个数用来交替更改数字的正负号
	private int j = 1;
	// 定义公平锁,用来是线程1和线程2交替执行
	ReentrantLock lock = new ReentrantLock(true);

	@Override
	public void run() {
		for(int i = 1; i < 100; i++){
			//获取锁
			lock.lock();
			try{
				System.out.println(Thread.currentThread().getName()+" "+i*j);
				j=-j;
			}finally{
				//释放锁
				lock.unlock();
			}
		}
	}
}

调用:

package com.thread.synchronizedDemo.lock;

public class TestLock {
	public static void main(String[] args) {
		//创建对象
		LockDemo lockDemo = new LockDemo();
		//创建两个线程,启动两个线程
		new Thread(lockDemo,"线程1").start();
		new Thread(lockDemo,"线程2").start();
	}
}

(2)采用wait()和notify()方法

public class Demo11 {
	public static void main(String[] args) {
		// 创建锁对象
		final Object object = new Object();
		// 创建线程A,开启线程
		new Thread(new Runnable() {

			@Override
			public void run() {
				for (int i = 1; i < 100; i++) {
					synchronized (object) {
						try {
							System.out.println(Thread.currentThread().getName() + " " + i);
							// 进入等待状态,并释放锁
							object.wait();// 无线等待状态
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
		}, "A").start();

		// 创建线程B
		new Thread(new Runnable() {

			@Override
			public void run() {
				for (int i = 1; i < 100; i++) {
					synchronized (object) {
						System.out.println(Thread.currentThread().getName() + " " + (-1) * i);
						// 唤醒线程A
						object.notify();
						/*
						 * try { Thread.sleep(1); } catch (InterruptedException
						 * e) { // TODO Auto-generated catch block
						 * e.printStackTrace(); }
						 */

						/**
						 * 为什么不能放在这个位置:
						 * 因为静态代码块执行完毕,虽然等待了1毫秒,但是还不会释放锁(sleep()方法不会释放锁的),
						 * 只有等静态代码块执行完毕的时候才开始释放锁,释放完锁,线程A和线程B会去争夺锁不一定
						 * 是A获得到锁,所以不会出现 1 -1 2 -2 3 -3 ....的效果,所以将这段代码写在锁释放完后
						 * 面,让其失去机会获取锁,然A获取到锁,执行,然后等待,释放锁,等待B线程唤醒,B抢到锁执行
						 * 代码,唤醒A,这样交替执行
						 */
					}
					
					//线程A和线程B交换执行关键所在
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}, "B").start();
	}
}

3、多线程中两个线程交替执行,一个输出偶数,一个输出奇数
使用volatile解决

class ThreadPrintDemo3{

  static volatile int num = 0;
  static volatile boolean flag = false;

  public static void main(String[] args) {

    Thread t1 = new Thread(() -> {
      for (; 100 > num; ) {
        if (!flag && (num == 0 || ++num % 2 == 0)) {

          try {
            Thread.sleep(100);// 防止打印速度过快导致混乱
          } catch (InterruptedException e) {
            //NO
          }

          System.out.println(num);
          flag = true;
        }
      }
    }
    );

    Thread t2 = new Thread(() -> {
      for (; 100 > num; ) {
        if (flag && (++num % 2 != 0)) {

          try {
            Thread.sleep(100);// 防止打印速度过快导致混乱
          } catch (InterruptedException e) {
            //NO
          }

          System.out.println(num);
          flag = false;
        }
      }
    }
    );
    t1.start();
    t2.start();

  }
}

4、字符翻转

class ReverseDemo {

  public static void main(String[] args) {

    String test = "abcdefg";
    //1、利用stringbuilder的reverse方法
    System.out.println(new StringBuilder(test).reverse());
	
	//2、利用转换数组的方法循环打印
    char[] arr = test.toCharArray();
    for (int i = arr.length - 1; i >= 0; i--) {
      System.out.print(arr[i]);
    }
  }
}

4、Web三大概念:cookie(记录前端数据),session(会话),application(请求)
(1)Session与cookie功能效果相同。Session与Cookie的区别在于Session是记录在服务端的,而Cookie是记录在客户端的。
(2)session指在服务器记录一系列状态;当访问服务器的时候,会在服务器的内存里开辟一块内存,这块内存为session,而这个内存是跟浏览器关联在一起(浏览器指的是浏览器窗口,或者是浏览器的子窗口,即只允许当前这个session对应的浏览器访问,就算是在同一个机器上新启的浏览器也是无法访问的。而另外一个浏览器也需要记录session的话,就会再启一个属于自己的session)。
(3)session的两种实现方式(也就是传递方式):第一种通过cookies实现。第二种通过URL重写来实现。
A、第一种是把session的id 放在cookie里面(cookie存放有临时的、有定时的,临时的就是当前浏览器什么时候关掉即消失,也就是说session本来就是当浏览器关闭即消失的,所以可以用临时的cookie存放。保存再cookie里的sessionID一定不会重复,因为是独一无二的。),当允许浏览器使用cookie的时候,session就会依赖于cookies;
B、当浏览器不支持cookie后,就可以通过第二种方式获取session内存中的数据资源,即重写URL的方式,如何重写URL?通过response.encodeURL()方法,encodeURL()的作用:转码、URL后面加入sessionID。
(4)session中的一些常用方法:
isNew():是否是新的Session,一般在第一次访问的时候出现
getid():拿到session,获取ID
getCreationTime():当前session创建的时间
getLastAccessedTime():最近的一次访问这个session的时间。
getRrquestedSessionid: 跟随上个网页cookies或者URL传过来的session
isRequestedSessionIdFromCookie():是否通过Cookies传过来的
isRequestedSessionIdFromURL():是否通过重写URL传过来的
isRequestedSessionIdValid():是不是有效的sessionID
5、synchronized(同步锁)与Lock
(1) Lock是一个接口,而Synchronized是关键字;
(2) Synchronized会自动释放锁,而Lock必须手动释放锁;
(3) Lock可以让等待锁的线程响应中断,而Synchronized不会,线程会一直等待下去;
(4) 通过Lock可以知道线程有没有拿到锁,而Synchronized不能;
(5) Lock能提高多个线程读操作的效率。
(6) Synchronized能锁住类、方法和代码块,而Lock是块范围内的;
6、Runnable与Thread
(1)其实Thread也就是实现了Runnable接口,提供了更多的方法而已。所以说Thread与Runnable并没有什么区别。如果硬要说有什么区别的话,那就是类与接口的区别,继承与实现的区别。
简单来说(1)效果上没区别,写法上的区别而已。
(2)没有可比性,Thread实现了Runnable接口并进行了扩展,我们通常拿来进行比较只是写法上的比较,而Thread和Runnable的实质是实现的关系,不是同类东西。
7、Java类集
(1)类集是Java实现的数据结构应用,核心接口有List、set、Map、Iterator、Enumeration;
(2)List子接口:根据索引号取的内容;而ArrayList(包装了数组的集合,且数组可变)、LinkedList(链表的实现,搜索数据时间复杂度为n)。
(3)set子接口:排序子类、HashSet与HashCode和equals
HashSet:重复的判断依靠的是hashCode和equals,无序;
(4)Map:map.Entry、Iterator输出、HashMap
8、Spring的工作原理,控制反转怎么实现
核心组成:IOC&DI(工厂设计)、AOP(代理设计、动态代理设计),针对XMl的解析处理采用的是DOM4J,其中anntation的时候必须有容器;简单说就是通过工厂设计实现反转,用DOM4J来解析XMl;
9、动态代理实现
直接使用InvocationHandler接口实现,同时利用proxy类设置动态请求对象;
10、springMVC可通过@scope=prototye来进行控制单实例或多实例;
11、面向对象的特征
封装、继承、多态、抽象

  • 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面(抽象只关注对象有哪些属性和行为)。
  • 封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
  • 继承:继承是从已有类得到继承信息创建新类的过程。提供继承的类叫父类(超类、基类)、得到继承的类叫子类(派生类)。
  • 多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。实现多态需要做两件事:1). 方法重写(子类继承父类并重写父类中的方法);2). 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为。
    12、Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type)和枚举类型(enumeration type),剩下的都是引用类型(reference type)。
    13、内存中的栈(stack)、堆(heap)和静态区(static area)的用法
    栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间;
    例如:String str = new String(“hello”);
    变量str放在栈上,用new创建出来的字符串对象放在堆上,而”hello”这个字面量放在静态区。
    14、、Math.round()的取值在参数上加0.5然后进行下取整。
    15、左移与右移
    2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方) 。
    16、构造器(constructor)是否可被重写(override):构造器不能被继承,因此不能被重写,但可以被重载。
    17、重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。
    18、JVM加载class文件的原理机制:JVM中类的装载是由类加载器(ClassLoader)和它的子类实现的,Java中的类加载器是一个重要的Java运行时系统组件,负责在运行时查找和装入类文件中的类。类的加载:–指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件。
    19、抽象类(abstract class)与接口(interface):
    -抽象类与接口都不能实例化,但可以定义抽象类和接口类型的引用;
    -一个类如果继承了某个抽象类或者实现了某个接口,则需要对其中的抽象方法全部进行实现,否则该类任然需要声明为抽象类;
    -抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的方法全部都是抽象方法;
    -抽象类的成员可以是private、public、protected、默认,而接口中的成员全都是public的;
    -抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量;
    -有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。
    20、Java和JavaSciprt。
    -java是静态语言,js是动态语言
  • 基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对象供设计人员使用。
  • 解释和编译:Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏览器几乎都使用了JIT(即时编译)技术来提升JavaScript的运行效率)
  • 强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行时检查推断其数据类型。
    21、Collection和Collections:Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
    22、线程的sleep()方法和yield()方法
    -sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
    -线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
    • sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
    • sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
      23、与线程同步以及线程调度相关的方法
    • wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
    • sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
    • notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
  • notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
    24、多线程实现
  • 继承Thread类;
  • 实现Runnable接口;
    两种方式都要通过重写run()方法来定义线程的行为
    25、事务的ACID
  • 原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;
  • 一致性(Consistent):事务结束后系统状态是一致的;
  • 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;
  • 持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。
    26、面向对象的”六原则一法则”
  • 单一职责原则:一个类只做它该做的事情,即“高内聚(低耦合)”;
  • 开闭原则:软件实体应当对扩展开放,对修改关闭(A、抽象,一个系统中如果没有抽象类或接口系统就没有扩展点;B、封装可变性,将系统中的各种可变因素封装到一个继承结构中);
  • 依赖倒转原则:面向接口编程;
  • 里氏替换原则:任何时候都可以用子类型替换掉父类型;
  • 接口隔离原则:接口要小而专,绝不能大而全;
  • 合成聚合复用原则:优先使用聚合或合成关系复用代码;
  • 迪米特法则:又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解(做到低耦合);
    27、几个常用的设计模式
  • 工厂模式:工厂类可以根据条件生成不同的子类实例,这些子类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作(多态方法)。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。
  • 代理模式:给一个对象提供一个代理对象,并由代理对象控制原对象的引用。
  • 适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起使用的类能够一起工作。
  • 单例模式:一个类只有一个实例,即一个类只有一个对象实例(懒汉式单例模式,线程不安全,致命的是在多线程不能正常工作;饿汉式单例模式,避免了多线程的同步问题);例如
    懒汉:
public class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static synchronized Singleton getInstance(){
        if (instance == null) instance = new Singleton();
        return instance;
    }
}

饿汉:

public class Singleton {
    private Singleton(){}
    private static Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
}

28、Servlet的运行过程
Web容器加载Servlet并将其实例化,Servlet生命周期开始,容器运行其init()方法进行Servlet的初始化—》请求到达时调用Servlet的service()方法,service()方法会根据需要调用与请求对应的doGet或doPost等方法—》当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的destroy()方法。
29、转发(forward)和重定向(redirect)
forward是容器中控制权的转向,是服务器请求资源,服务器直接访问目标地址的URL,把那个URL 的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。redirect就是服务器端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,因此从浏览器的地址栏中可以看到跳转后的链接地址,很明显redirect无法访问到服务器保护起来资源,但是可以从一个网站redirect到其他网站。
30、JSP中的四种作用域page、request、session和application

  • page代表与一个页面相关的对象和属性。
  • request代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。
  • session代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。
  • application代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李@凌峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值