目录
String Stringbuffer StringBulider
Class类文件结构(待补充)
2019.9.25====================================================================================
回顾类型转换:
1.自动类型转换(隐式转换)--小转大
2.强制类型转换 ---大转小
一个经典的面试例子:
short s1=1; s1=s1+1;//.eg1 short s2=1; s2+=1;
第一个例子是错误的,so 为啥?这就涉及到强制转换的问题,大-->小,s1+1的结果为Int类型,虽然s1是short类型,但是java在对两个不同类型的变量进行运算的时候,会自动对小容量的变量进行精度提升。在这个时候如果再把大类型的变量赋值给小类型变量就会报精度丢失的错误。如果改正就应该(short)(s1+1)
第二个例子是正确的因为s2+=1时就会进行窄化转换所以没问题。
equals对比==
在jvm的内存中,分为堆和栈,引用类型的存储有别于基本数据类型。创建对象时,构造函数会在堆中构造对象数据,而同时在栈中生成引用。而基本数据类型一般认为存储在栈中。
而equals对比的是值,而==对比的是内存地址
引深例子:String s="abcd" 与 String s=new String("abcd");
String s="abcd";是一种不需要new就可以创建对象的一种方式,它存储在字符串常量池,会先在内存中寻找“abcd”,找到了就返回引用的对象,没找到就创建一个。
String s=new String(“abcd”);只要一调用s就会创建一个对象。
String s="abcd"; String w="abcd"; System.out.println(s==w);//true System.out.println(s.equals(w));//true String x=new String("abcd"); System.out.println(s==x);//false System.out.println(s.equals(x));//true
wait()与sleep()区别?
1.来自不同的类 wait()是Object,而sleep()是Thread。
2.wait方法会释放锁。
3.Wait在同步方法或者同步控制块中使用,sleep可以在任意位置。
4.sleep使用时需要捕获异常。
String Stringbuffer StringBulider
String是java的基础类。
StringBuffer是对String的包装类 效率较低但是线程安全
StringBuilder是对StringBuffer的拓展 提高了效率但是线程不安全
创建线程有几种方法?
1.继承Thread类,重写run()方法
2.实现runnable接口,重写run( )方法
2019.9.26更===================================================================================
排序方法?
for(int j=0;j<a.length-1;j++) { for(int i=j+1;i<a.length;i++) { if(a[j]>a[i]) { int temp=a[j]; a[j]=a[i]; a[i]=temp; } } } //直接排序: //流程助记:每轮首位依次比,数小就往前面请。首数定位往后移,反复如此做排序。 //外循环作首位定位,内循环作依次比较。
for (int j = 0; j < a.length; j++) { for (int i = 0; i < a.length-j-1; i++) { if(a[i]>a[i+1]){ int temp = a[i]; a[i] = a[i+1]; a[i+1] = temp; } } } //冒泡排序: //流程助记:两两比较,数大后挪。后界向前,循环反复。 //外循环控制后面边界前移。 //内循环控制每轮比较次数。
当数据量足够大的时候,快速排序的效率更高
反射?
一个eg类,Person
public class Person { private int id; String name; public Person(int id,String name){ this.id = id; this.name = name; } public Person(){} public void eat(int num){ System.out.println(name+"吃很"+num+"斤饭"); } private static void sleep(int num){ System.out.println("明天睡上"+num+"小时"); } public static void sum(int[] arr){ System.out.println("长度是:"+ arr.length); } @Override public String toString() { return " 编号:"+ this.id +" 姓名:"+ this.name; } }
反射:当一个字节码文件加载到内存中去的时候,JVM会对该字节码进行解剖,然后去创建一个Class的对象,把字节码文件信息全部都存储到该Class对象中去,只要获取到Class对象,我们就可以字节码对象设置对象的属性或者调用对象的方法等操作...
注意:在反射技术中一个类的任何成员都有对应的类进行描述。eg:成员变量(Field)方法 --->method类
//获取Class对象的方式,推荐使用 Class clazz=Class.forName("main.Class.Person"); System.out.println(clazz); //方式2 // Class class1=Person.class; // System.out.println("clazz=class1?"+(clazz==class1)); //方式3 // Class class2=new Person(1, "jordan").getClass(); // System.out.println("clazz=class2?"+(clazz==class2));
反射获取构造方法
//通过class对象获取对应的构造方法,公共的构造方法 Constructor[] constructors=clazz.getConstructors(); for (Constructor constructor : constructors) { System.out.println("公共的构造方法"+constructor); } //通过Class对象获取对应的构造方法,所有构造方法,包括私有的在内 Constructor[] constructors2=clazz.getDeclaredConstructors(); for (Constructor constructor : constructors2) { System.out.println("所有的构造方法"+constructor); } //getConstructor获取单个指定的构造方法 Constructor constructor=clazz.getConstructor(int.class,String.class); System.out.println("获取单个指定的构造方法"+constructor); Person person =(Person) constructor.newInstance(1,"kobe"); System.out.println("获取单个指定的构造方法的实例"+person); //getConstructor获取无参单个指定的构造方法 // Constructor constructor2=clazz.getConstructor(null); // System.out.println("获取单个指定的构造方法"+constructor2); // Person person2 =(Person) constructor2.newInstance(null); // System.out.println("获取单个指定的构造方法的实例"+person2);
反射获取 Method
Method[] methods=clazz.getMethods(); for (Method method : methods) { System.out.println("所有的公有方法"+method); } Method[] methods2 =clazz.getDeclaredMethods(); for (Method method : methods2) { System.out.println("所有的方法,但不包括父类方法"+method); } System.out.println("=======调用方法=========="); Person p=new Person(0, "ssa"); Method method=clazz.getMethod("eat", int.class); method.invoke(p, 3); System.out.println("=======调用私有方法=========="); Method method2=clazz.getDeclaredMethod("sleep", int.class); //设置访问权限允许访问 method2.setAccessible(true); method2.invoke(null, 3); System.out.println("=======调用sum方法=========="); Method m3 = clazz.getDeclaredMethod("sum", int[].class); m3.invoke(p, new int[]{12,3,4});
反射获取成员变量
//获取到所有的成员变量 Field[] fieldss=clazz.getFields(); for (Field field : fieldss) { System.out.println("所有的公有的成员变量"+field); } //获取到所有的成员变量 Field[] fields=clazz.getDeclaredFields(); for (Field field : fields) { System.out.println("所有的成员变量"+field); } //设置成员变量的值 Field field=clazz.getDeclaredField("id"); field.setAccessible(true); field.set(p, 24); System.out.println("设置过后的属性为"+p);
2019.9.27更=========================================================================================
线程同步的方式?
1.synchronized关键字
同步方法(特点:开销较大,尽量减少同步的内容,没必要同步整个方法)若方法是静态的,线程锁定的就是这个类对应的java.lang.Class对象
同步块:锁定一个对象,对同步块中的代码进行同步。
2.wait()与notify
wait使一个线程进入等待状态,并且释放所持有对象的lock.
sleep使一个正在运行的线程处于睡眠状态,调用此方法捕捉interruptedException异常。
notify 唤醒一个等待的线程,由jvm决定唤醒哪个线程,而不是按照优先级
3.特殊变量volatile.
为域变量提供了一种免锁机制,相当于告诉jvm该域可能会被其他线程更新,使用时就会重新计算,而不是使用寄存器中的值。volatile不会提供任何原子操作,用来修饰final类型的变量。
4.重入锁ReentrantLock,可重入,互斥
lock()获得锁,unlock()释放锁
2019.9.29更==========================================================================================
JVM?底层
2019.10.10日更================================================================================
左边是线程公有的区域,右边是线程私有的区域
程序计数器:作用:当前线程的所执行的字节码的行号指示器。(原理:通过改变这个计数器的值来选取下一条需要执行的字节码指令) 线程轮流切换并分配处理器执行时间,使线程切换后能恢复到正确的执行位置。若正在执行Native方法,则这个计数器的值为空(Undefined).。唯一没有OutofMemoryError的区域。
java虚拟机栈:生命周期与线程相同。java方法被执行的时候会创建一个栈帧。每一个方法被调用直到执行完成,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。局部变量表用来存放编译时期的各种基本数据类型,他所需的内存空间是在编译期间就完成分配的。存在StackOverflowError和outOfMemoryError两种错误情况。
本地方法栈:为虚拟机使用到的Native方法服务。存在StackOverflowError和outOfMemoryError两种错误情况。
java堆:被所有线程共享的的一片内存区域,在虚拟机启动时创建。此内存区域唯一的目的就是存放对象实例,是垃圾收集器管理的主要区域。存在outOfMemoryError。
方法区:各个线程共享的区域的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。
运行时常量池:他是属于方法区的一部分,class文件中的常量池,用于存放编译期生成各种字面量和符号引用。这部分内容将在类加载后存放到方法区的运行时常量池中。通常,翻译出来的直接引用也会存储在运行时常量池中去。
Class类文件结构
Class文件是一组以8位字节为基础单位的二进制流。Class文件采用一种类似C语言结构体的伪结构来存储,他只有两种数据类型:无符号数和表。
。。。。(待补充)
基本类型与包装类型的区别?
1.包装类型可以为null,而基本类型不可以
class Writer { private Integer age; private String name; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
来自ali手册上的一句话,数据库的查询结果可能是 null,如果使用基本类型的话,因为要自动拆箱(将包装类型转为基本类型,比如说把 Integer 对象转换成 int 值),就会抛出
NullPointerException
的异常。2.包装类型可用于泛型,而基本类型不可以
因为泛型在编译时会进行类型擦除,最后只保留原始类型,而原始类型只能是 Object 类及其子类——基本类型是个特例
3.基本类型比包装类型更高效
基本类型在栈中直接存储的具体数值,而包装类型则存储的是堆中的引用
4.两个包装类型的值可以相同,但却不相等。
Integer chenmo = new Integer(10); Integer wanger = new Integer(10); System.out.println(chenmo == wanger); // false System.out.println(chenmo.equals(wanger )); // true
两个包装类型在使用“==”进行判断的时候,判断的是其指向的地址是否相等。chenmo 和 wanger 两个变量使用了 new 关键字,导致它们在“==”的时候输出了 false。
自动拆箱与自动装箱->
包装类型->基本类型 拆箱
基本类型->包装类型 装箱
// 1)基本类型和包装类型 int a = 100; Integer b = 100; System.out.println(a == b); // 2)两个包装类型 Integer c = 100; Integer d = 100; System.out.println(c == d); // 3) c = 200; d = 200; System.out.println(c == d);
第一段代码 true