###1.java基础
JAVA 的基本数据类型有哪些 ? String 是不是基本数据类型 ?
| 数据类型 | 关键字 | 内存占用 | 取值范围 |
| :————–: | :—-: | :–: | :–: |
| 字节型 | byte | 1 | -2的7次方~2的7-1(-128~127) |
| 短整型 | short | 2 | -2的15次方~2的15-1(-32768~32767) |
| 整型 | int | 4 | -231次方~2的31次方-1 |
| 长整型 | long | 8 | -2的31次方~2的31次方-1 |
| 单精度浮点数 | float | 4 | 1.4013E-45~3.4028E+38 |
| 双精度浮点数 | double | 8 | 4.9E-324~1.7977E+308 |
| 字符型 | char | 2 | 0-65535(0~2的16次方-1) |
| 布尔类型 | boolean | 1 | true,false |String不是基本数据类型
1个字节8个二进制位
说说& 和&& 的区别
1.共同点
&和&&都可以用作逻辑运算符,表示逻辑与(and),当运算符两边的表达式的结果都
为 true 时,整个运算结果才为 true,否则,只要有一方为 false,则结果为 false。2.不同点
&&还具有短路的功能,即如果第一个表达式为 false,则不再计算第二个表达式,例如,对
于 if(str != null && !str.equals(“”))表达式,当 str 为 null 时,后面的表达式不会执行,所以不
会出现 NullPointerException 如果将&&改为&,则会抛出 NullPointerException 异常。当然我们也应该用if(str != null && !“”.equals(str))
&还可以用作位运算符,当&操作符两边的表达式不是 boolean 类型时,&表示按位与操作。
short s1 = 1; s1 = s1 + 1; 有什么错? short s1 = 1; s1 += 1; 有什么错?
对于 short s1 = 1; s1 = s1 + 1; 由于 s1+1 运算时会自动提升表达式的类型,所以结果是 int 型,
再赋值给 short 类型 s1 时,编译器将报告需要强制转换类型的错误。
对于 short s1 = 1; s1 += 1;由于 += 是 复合赋值操作符 ,java 编译器会自动地将所执行计算的结果转型为其左侧变量的类型 ,s1+=1等效于 s1=(short)(s1+1) ,因此可以正确编译但是请不要将复合赋值操作符作用于byte、short或char类型的变量 。
因为S1是short型的,占2个字节,而100000是int型的,占4个字节。在两个类型的值相加的时候,会发生自动类型的提升,要不然数据也装不下呀。如果使用+=,会造成数值越界。
char 型变量中能不能存贮一个中文汉字?
char 型变量是用来存储 Unicode 编码的字符的,unicode 编码字符集中包含了汉字,所以,
char 型变量中当然可以存储汉字啦。不过,如果某个特殊的汉字没有被包含在 unicode 编码
字符集中,那么,这个 char 型变量中就不能存储这个特殊汉字。补充说明:unicode 编码占
用两个字节,所以,char 类型的变量也是占用两个字节。使用 final 关键字修饰 一个变量时,是引用不能变,还是引用的对象不能变 ?
使用 final 关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容
还是可以改变的。
例如,对于如下语句:
final StringBuffer a=new StringBuffer(“immutable”);
执行如下语句将报告编译期错误:
a=new StringBuffer(“”);
但是,执行如下语句则可以通过编译:
a.append(“ broken!”);“==”和 和 equals 方法究竟有什么区别?
1)对于==,比较的是值是否相等
如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
如果作用于引用类型的变量,则比较的是对象的地址是否相同
2)equals方法,在Object类中,equals方法是用来比较两个对象的引用是否相等,即是否指向同一个对象 。
public boolean equals(Object anObject) {//jdk1.8的equals部分源码 if (this == anObject) { return true; } ...
如String类当中重写了equals方法,比较的字符串的内容是否相同。
注意:equals方法不能作用于基本数据类型的变量。
String类jdk1.9开始是private final byte value[];
静态变量和实例变量的区别?
###1. 0.面向对象
封装
继承
多态
抽象
####1.1.重用API
####1.2.框架
####1.3.多线程
synchronized锁静态方法相当于锁的是class文件(类)
锁普通方法锁的是堆内存的对象
####1.4.反射
#####1.4.1类加载的过程
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化
就是给属性赋值
#####1.4..2类加载时机 -【面试题】
Ø 加载的原则-**用到的时候才加载字节码**
Ø 比如:
n 创建类的实例 new Student
n 访问类的静态变量,或者为静态变量赋值 Intergar.MAX_VALUE;
n 调用类的静态方法
n 初始化某个类的子类
n 直接使用java.exe命令来运行某个主类
n 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
#####1.4.3类加载器
类加载器的概述
负责将.class文件加载到内存中,并为之生成对应的Class对象。虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
1.4.4反射
https://blog.csdn.net/sinat_38259539/article/details/71799078
反射就是把java类中的各种成分映射成一个个的Java对象
反射是在java.lang.reflect这个包中
* 获取Class对象的方式:
1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
* 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
2. 类名.class:通过类名的属性class获取
* 多用于参数的传递
3. 对象.getClass():getClass()方法在Object类中定义着。
* 多用于对象的获取字节码的方式
* 结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
public class Fanshe {
public static void main(String[] args) {
// 第一种方式获取Class对象
Class stuClass1 = Student.class;
//第二种方式获取Class对象
try {
Class stuClass2 = Class.forName("com.反射.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//第三种方式获取Class对象
Student stu3 = new Student();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象
}
}
1.4.4.1获得构造方法
1.如果要使用反射,先要获取字节码对象
2.通过字节码对象的getConstructor()可以获取到构造方法对象
3.构造方法对象(Contructor),有个newInstance方法创建这个字节码对象
4.如果是私有的需要先调用setAccessible(true)设置访问权限,
5.反射的作用一般是用于写框架(ssh,ssm)
package com.反射;
/**
* @author wangben
* @Title: Student
* @Package: com.反射
* @ProjectName javase-code
* @Description: TODO
* @date 2019-01-27 13:25 星期日
*/
public class Student {
private String name;
private int age;
private Student(String name, int age) {
this.name = name;
this.age = age;
}
private String getName() {return name;}
private void setName(String name) {this.name = name;}
private Student() {}
private void add(int money){
System.out.println("302加钱"+money);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
获得构造方法Constructor
批量的方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
获取单个的方法,并调用:
这里需要的是一个参数的类型,切记是类型。如果是无参的构造方法所以类型是一个null,也可以不写留空
public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
调用构造方法:
Constructor-->newInstance(Object... initargs)
* 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法(前提是构造器不能是私有的)
Class clz = Class.forName("com.反射.Student");
Student s =(Student)clz.newInstance();
//以私有的,参数为(String name)的构造器为例
//1获取字节码class对象
Class clz = Class.forName("com.反射.Student");
//获取私有的参数为(String name)构造方法
Constructor con = clz.getDeclaredConstructor(String.class,int.class);
System.out.println(con);
//调用构造方法
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
Object obj = con.newInstance('栗子',25);
System.out.println(obj);
---------------------
private fanshe.Student(java.lang.String,int)
Student{name='栗子', age=25}
注意
:原始类型对应的虚拟机中的class实例和封装类对应的class实例是不相同的。
如:
int 对应的class实例为 int.class 或者 Integer.TYPE,但是 Integer 对应的 class 实例为 Integer.class
######1.4.4.2获得属性
1.Class的getField(String)方法可以获取类中的指定字段(可见的),
2.如果是私有的,可以用getDeclaedField(“name”)方法获取
3.通过set(obj, “李四”)方法可以设置指定对象上该字段的值
4.如果是私有的需要先调用setAccessible(true)设置访问权限,
5.调用get(obj)可以获取指定对象中该字段的值
* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的 public修饰的成员变量
* Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name) 获取指定名称的 不考虑修饰符
私有无参构造,私有name属性
package com.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Fanshe {
public static void main(String[] args) {
try {
//1获取字节码class对象
Class clz = Class.forName("com.反射.Student");
//2.获得私有的无参构造
Constructor con = clz.getDeclaredConstructor();
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
//3 通过构造方法创建对象
Object o = con.newInstance();
//4.获取私有name属性
Field nameField = clz.getDeclaredField("name");
System.out.println(nameField);
//5.通过反射给私有属性赋值
nameField.setAccessible(true);//设置私有属性可以访问
nameField.set(o, "栗子");
//6.通过反射获取私有属性name的值
Object value = nameField.get(o);
System.out.println(value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
---------------------
private java.lang.String com.反射.Student.name
栗子
######1.4.4.3获得方法
1.反射中通过Method类描述方法【构造方法:Contructor,字段:Field】
2.通过Class的getMethod可以获取一个方法
3.通过getDeclaredMethod可以获取私有方法
4.如果要调用私有方法,设置访问权限setAccessible
5.method.invoke(list, “栗子”);
* Method[] getMethods() 获取所有"公有方法";(包含了父类的方法也包含Object类)
* Method[] getDeclaredMethods() 获取所有的成员方法,包括私有的(不包括继承的)
参数:
name : 方法名;
Class ... : 形参的Class类型对象
* Method getMethod(String name, 类<?>... parameterTypes)
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)
package com.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 获取Class对象的三种方式
* 1 Object ——> getClass();
* 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
* 3 通过Class类的静态方法:forName(String className)(常用)
*
*/
public class Fanshe {
public static void main(String[] args) {
try {
//1获取字节码class对象
Class clz = Class.forName("com.反射.Student");
//2.获得私有的无参构造
Constructor con = clz.getDeclaredConstructor();
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
//3 通过构造方法创建对象
Object o = con.newInstance();
//4.获取私有name属性
Field nameField = clz.getDeclaredField("name");
System.out.println(nameField);
//5.通过反射给私有属性赋值
nameField.setAccessible(true);//设置私有属性可以访问
nameField.set(o, "栗子");
//6.通过反射获取私有属性name的值
Object value = nameField.get(o);
System.out.println(value);
//7.获得私有方法
Method add = clz.getDeclaredMethod("add", int.class);
add.setAccessible(true);//设置私有属性可以访问
add.invoke(o,50);
} catch (Exception e) {
e.printStackTrace();
}
}
}
----------------------------
private java.lang.String com.反射.Student.name
栗子
302加钱50
1.4.4.4其他
File file = new File(Xml01.class.getClassLoader()
.getResource("bean.xml").getPath());
E:\idea_project\javase-code\out\production\day01\bean.xml
E:\idea_project\javase-code\out\production\day01 //不带参数
---------------------------
File f = new File(Test.class.getResource("/").getPath());
E:\idea_project\javase-code\out\production\day01
E:\idea_project\javase-code\out\production\day01\com\wang //不带参数,反而带包
---------------------
String name = Test.class.getName();
com.wang.Test
------------------
String name = Test.class.getSimpleName();
Test
----------------
反射越过泛型
//1.声明泛型集合
List<Integer> list = new ArrayList<Integer>();
list.add(110);
list.add(120);
list.add(130);
//list.add("lizi");
//2.通过反射往集合添加字符串
//2.1 获取字节码对象(Class)
Class clz = list.getClass();
//2.2 通过反射获取方法
Method method = clz.getMethod("add", Object.class);
//2.3 调用方法
method.invoke(list, "lizi");
System.out.println(list);
###2.javaweb
###3.框架
3.1.spring
介绍一下你对spring框架的理解
说一下spring的加载过程
ioc与di的区别
####3.2.mybatis
####3.3.springmvc
####3.4.springboot
4.编程
####4.1 单例模式
https://blog.csdn.net/mnb65482/article/details/80458571
定义:保证一个类仅有一个实例,并提供一个全局的访问点。
应用场景:想确保任何情况下都绝对只有一个实例
需要频繁实例化然后销毁的对象(数据连接池,多线程线程池)网站的计数器,一般也是采用单例模式实现,否则难以同步
数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
优点
1.对于一些需要频繁创建和销毁的对象来说可以提高系统的性能。
2.实现了对唯一实例访问的可控缺点
1.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2.如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失。
3..滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出。单例代码
单例模式分类
懒汉式
2. 饿汉式
- 双
单例模式要素:
a. 私有构造方法
b. 私有静态引用指向自己实例
c. 以自己实例为返回值的公有静态方法
为什么是静态变量和静态方法?
单例就是说一个类仅有一个实例。
首先构造器是私有的。私有的构造器防止其他程序创建该类的对象,
我们就需要创建一个私有的引用指向自己的实例,提供一个公共的获取单例的方法
调用类中方法有两种方式
①创建类的一个对象,用对象去调用方法;②使用类名直接调用类中方法。
显然第一种情况不能用,只能使用第二种方法。而想要使用类名直接调用类中方法,类中方法必须是静态的,而静态方法不能访问非静态成员变量,因此私有引用也必须是静态的。
4.1.1饿汉式与懒汉式
1、饿汉式:(比较饿)在单件模式类被加载的时候,单件模式实例就已经被创建。
2、懒汉式:(比较懒)当程序第一次访问单件模式实例时才进行创建。
如何选择:如果单件模式实例在系统中经常会被用到,饿汉式是一个不错的选择。
反之如果单件模式在系统中会很少用到或者几乎不会用到,那么懒汉式是一个不错的选择。
懒汉式
懒汉模式在方法被调用后才创建对象,以时间换空间,在多线程环境下存在风险。
多线程时不安全
public class SingleTon{
private static SingleTon INSTANCE = null;
private SingleTon(){}
public static SingleTon getInstance() {
if(INSTANCE == null){
//多线程时,假如多个线程进入if里会创建多个对象
INSTANCE = new SingleTon();
}
return INSTANCE;
}
}
改进
加锁
public class SingleTon{
private static SingleTon INSTANCE = null;
private SingleTon(){}
//synchronized加锁解锁消耗资源,而且加在静态方法上相当于锁类,范围大
public synchronized static SingleTon getInstance() {
if(INSTANCE == null){
INSTANCE = new SingleTon();
}
return INSTANCE;
}
}
再改进
双重检查锁(DCL模式)
只有在对象需要被使用时才创建,第一次判断 Bean== null为了避免非必要的加锁,当第一次加载时才对实例进行加锁再实例化。这样既可以节约内存空间,又可以保证线程安全。但是,由于jvm存在乱序执行功能,DCL也会出现线程不安全的情况。具体分析如下:
1.在堆内存开辟内存空间。
2.在堆内存中实例化SingleTon里面的各个参数(初始化对象)。
3.把对象指向堆内存空间。
由于jvm存在乱序执行功能,所以可能在2还没执行时就先执行了3,如果此时再被切换到线程B上,由于执行了3,INSTANCE 已经非空了,会被直接拿出来用,这样的话,就会出现异常。这个就是著名的DCL失效问题。
volatile确保INSTANCE每次均在主内存中读取。
public class Bean {
//volatile保证线程对其他线程的可见性
//为了防止多线程指令重排序的问题
private volatile static Bean bean = null;
private Bean(){ }
public static Bean getBean(){
if (bean == null){
synchronized(Bean.class) {
if (bean == null) {
bean = new Bean();
}
}
}
return bean;
}
}
饿汉式
饿汉模式在类被初始化时就已经在内存中创建了对象,以空间换时间,故不存在线程安全问题 。
public class SingleTon{
private static SingleTon INSTANCE = new SingleTon();
private SingleTon(){}
public static SingleTon getInstance(){ return INSTANCE; }}
静态内部类
外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。即当SingleTon第一次被加载时,并不需要去加载SingleTonHoler,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
//基于类的初始化延时加载实现
public class SingleTon{
private SingleTon(){}
private static class SingleTonHoler{
private static SingleTon INSTANCE = new SingleTon();
}
public static SingleTon getInstance(){
return SingleTonHoler.INSTANCE;
}
}
//由于是静态内部类的形式去创建单例的,故外部无法传递参数进去,例如Context这种参数,
所以,我们创建单例时,可以在静态内部类与DCL模式里自己斟酌
暴力反射
类加载时就创造对象的饿汉式,静态内部类(加载内部类时创建对象),
暴力反射饿汉式代码,静态内部类方式同理,不做重复。
public static void main(String[] args) throws Exception {
//反射获得
Class<?> c1 = Class.forName("com.反射.SingleTon");
Constructor declaredConstructor = c1.getDeclaredConstructor();
//setAccessible值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
declaredConstructor.setAccessible(true);
SingleTon singleTon = (SingleTon)declaredConstructor.newInstance();
//通过面向外部公共访问方法获得
SingleTon instance = SingleTon.getInstance();
System.out.println(instance);
System.out.println(singleTon);
System.out.println(instance== singleTon);
}
----------------------------
com.反射.SingleTon@677327b6
com.反射.SingleTon@14ae5a5
false
防止代码
饿汉式
public class SingleTon{
private static SingleTon INSTANCE = new SingleTon();
private SingleTon(){
if(INSTANCE!=null){
throw new RuntimeException("单例模式禁止反射");
}
}
public static SingleTon getInstance(){ return INSTANCE; }}
--------------------
Exception in thread "main" java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.RuntimeException: 单例模式禁止反射
at com.反射.SingleTon.<init>(SingleTon.java:15)
... 5 more
懒汉时好像不能防止,有时间再看
即使加了判断,如果反射线程先进入 private SingleTon()里,还没创建对象,就不会RuntimeException
public class SingleTon{
private SingleTon(){
//如果反射线程先进入,还没创建对象,就不会RuntimeException
if(INSTANCE!=null){
throw new RuntimeException("单例模式禁止反射");
}
}
private static SingleTon INSTANCE = null;
public static SingleTon getInstance() {
if(INSTANCE == null){
INSTANCE = new SingleTon();
}
return INSTANCE;
}
}
枚举实现单例
即可防止暴力反射,序列化也咩问题,多线程可可以
package com.设计模式.单例模式;
public enum EnumSingleton {
INSTANCE;
private Object date;
public Object getDate() {
return date;
}
public void setDate(Object date) {
this.date = date;
}
public static EnumSingleton getEnumSingleton() {
return INSTANCE;
}
}
4.2代理模式
4.2.1 静态代理
静态代理的实现比较简单,代理类通过实现与目标对象相同的接口,并在类中维护一个代理对象。通过构造器塞入目标对象,赋值给代理对象,进而执行代理对象实现的接口方法,并实现前拦截,后拦截等所需的业务功能。
https://www.jb51.net/article/124323.htm