java基本类型变量
每个变量占据了一定的内存位置,声明一个变量时,就是在告知编译器这个变量可以存放什么类型(占据多少个字节的内存)。
1.定义:可利用关键字声明的变量就是基本类型变量。对于基本类型变量来说,对应内存所存储的值就是基本类型值。
2.八大基本类型变量
6种数字类型(byte/short/int/long/float/double)、1种字符型(char)、1种布尔型(boolean)
引用数据类型
引用数据类型:类(Class)、接口(Interface)、数组(Array)
所有引用数据类型的默认值都为null。
注意:字符串不是基本类型变量,字符串“MadJieJie”就相当于构造器new String()那样的实例,
大家可以像指针那样理解成为引用类型变量实际上存储的只是地址
基本数据类型与引用数据类型的区别
以byte基本类型为例:
byte a = 1;
byte b = 2;
a=b
对于基本类型变量而言,就是将一个变量的实际值赋给另一个变量,实际上就是将变量内存里所有的位上的二进制替换掉。
以String是引用类型为例:
String object1 = “MadJieJie”;
String object2 = “MadJieJie”;
object1 = object2; //两者引用值相等之后
当两者引用值相等之后,object1的引用就指向了object2对象,那么原来object1的内存是不会造成内存泄漏的,因为object1对象失去了所有的引用,它会被系统当作垃圾回收,这是JAVA的内存回收机制,但请注意,若object1还有其他引用变量指向它就不被回收。
对于引用类型变量而言,就是将一个变量的引用赋给另一个变量,实际上就是替换了对象的基址。
注意:
判断它们存储的字符串是否一样时,往往使用object1 == object2;判断的是地址
可以使用equals()方法来比较它们存储的字符串数据是否相同。
原生类指未被实例化的类,数组一般指实例化,被分配空间的类,不属于原生类。
原生类指的是原生数据类型,又叫做基本数据类型,主要有byte,short,int,long,float,double,char,boolean
对象的特点是封装了一些数据,同时提供了一些属性和方法,从这个角度来讲,数组是对象
2.
ArrayList
是实现List 接口的大小可变数组
选A,元素在集合中有序,指的是元素插入过程中记录了元素的插入顺序。
B.不可改变
C.唯一。错List中元素可重复,Set不可重复
D.键是唯一
E.线程同步。Synchronized属性指示当前的ArrayList实例是否支持线程同步,而ArrayList.Synchronized静态方法则会返回一个ArrayList的线程同步的封装。
如果使用非线程同步的实例,那么在多线程访问的时候,需要自己手动调用lock来保持线程同步
3.switch case
需要重点强调的是,当和某个整型数值匹配成功后,会执行该分支以及后面所有分支的语句,包括default。
4.
抽象类
抽象类不可以有方法体【去掉后面的{}】,而且缺少返回类型。
就应该这样:abstract int sum (int x, int y);
5.
对于&& :当第一个条件不满足时,后面的就不再计算
对于|| :当第一个条件满足时,后面的条件就不参与运算
6.
new String(“这是要转换的字符串”.getBytes(“ISO-8859-1”),“GB2312”);
也就是说: getBytes将(要转换的字符串)根据自己设置的编码格式ISO-8859-1读出来; 按String方法设置的编码格式GB2312保存起来; 成为一个编码格式为GB2312的字符串
答案:good and gbc
实参和形参
调用函数change传入(对象,字符串),也就是一个将实参的值赋给形参,一个将实参的地址赋给形参。
"change"方法运行时,第一条语句改变的是方法中形参的值,对实参没影响,第二条语句由于是直接改变的ch所指地址的值,所以把实参也进行了改变。
扩展:
char和string
- char和string的区别:
(1)char是表示的是字符,定义的时候用单引号,只能存储一个字符。例如; char=‘d’.
(2)String表示的是字符串,定义的时候用双引号,可以存储一个或者多个字符。例如:String=“we are neuer”。
(3)char是基本数据类型,而String是个类,属于引用数据类型。String类可以调用方法,具有面向对象的特征。
相互转换: - String转char:(1)String类的charAt()方法用于获取指定索引处的单个字符(2)String.toCharArray()来让String类型转化为char类型,返回值是char[],可以得到将包含整个String的char数组。
String str ="CSDN";
//方法一
char c1 = str.charAt(0);
System.out.println(c1);
//方法二
char[] value = str.toCharArray();
char c2=value[1];
System.out.println(c2);
char类型转化为String类型:
1.转换效率最快的方法用String.valueOf()来直接转换
2.将一个char数组转换成String,其中String.valueOf()中放的实际是数组
3.Character.toString(char)方法实际上直接返回String.valueOf(char)
4.new一个Character对象,用引用类型对象来调用toString()方法
(1)可变与不可变:String是不可变字符串对象,StringBuilder和StringBuffer是可变字符串对象(其内部的字符数组长度可变)。
(2)是否多线程安全:String中的对象是不可变的,也就可以理解为常量,显然线程安全。StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,只是StringBuffer 中的方法大都采用了synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是非线程安全的。
3. “String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。
4. 字符串常量池
我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。
如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。
由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串。
5. 使用String不一定创建对象
在执行到双引号包含字符串的语句时,如String a = "123",JVM会先到常量池里查找,如果有的话返回常量池里的这个实例的引用,否则的话创建一个新实例并置入常量池里。所以,当我们在使用诸如String str = "abc";的格式定义对象时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。**只有通过new()方法才能保证每次都创建一个新的对象**。
- equals和==
(1)对于==,如果作用于基本数据类型的变量(byte,short,char,int,long,float,double,boolean ),则直接比较其存储的"值"是否相等;如果作用于引用类型的变量(String),则比较的是所指向的对象的地址(即是否指向同一个对象)。
(2)equals方法是基类Object中的方法,判断否指向同一个对象。
(3)String类对equals方法进行了重写,用来比较指向的字符串对象所存储的字符串是否相等。其他的一些类诸如Double,Date,Integer等,都对equals方法进行了重写用来比较指向的对象所存储的内容是否相等。
String str1="aaa";
String str2="aaa";//
String str3=new String("aaa");//new 的新对象
String str4=new String("aaa");
System.out.println(str1==str2);// true 相同地址
System.out.println(str1==str3);// false 不同地址
System.out.println(str3==str4);// false 不同地址
System.out.println(str3.equals(str4));//true 值相同
-
Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。
所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
class Value{
public int i=15;
}
public class Test {
public static void main(String argv[]) {
Test t = new Test();
t.first();
}
public void first() {
int i = 5;
Value v = new Value();
v.i = 25;
second(v, i);
System.out.println(v.i);//first方法中的v的指向未改变过
}
public void second(Value v, int i) {//这里将引用变量v拷贝了一份,即多了一个引用变量v,v指向与first方法中的v一样的对象
i = 0;
v.i = 20;//改变拷贝的v指向的对象的i值,由于此时v指向与first方法中的v一样的对象,所以first方法中的对象i值变为20
Value val = new Value();//创建一个新对象
v = val;//改变拷贝的v指向的对象,此时拷贝的v与val指向同一个对象,而first方法中的v的指向是不变的
System.out.println(v.i + " " + i);
}
}
所以打印结果为:
15 0 20
这个重点在于:对引用变量(即对象)是以地址的形式存储,相当于指针指向对象地址位置。
在调用函数中:传入的对象是该对象引用的拷贝,相当于创建一个新指针指向对象地址
最先初始化的是静态域:
即静态变量、静态块和静态方法,其中需要初始化的是静态变量和静态块.而他们两个的初始化顺序是靠他们俩的位置决定的!
所以本题初始化顺序是:t1,t2,静态块
流程:
(1)开始时JVM加载B.class,对所有的静态成员进行声明,
(2)t1 t2被初始化为默认值,为null,又因为t1 t2需要被显式初始化,所以对t1进行显式初始化,初始化代码块→构造函数(调用默认的构造函数即本题中 的构造块),
(3)咦!静态代码块咋不初始化?因为在开始时已经对static部分进行了初始化,虽然只对static变量进行了初始化,但在初始化t1时也不会再执行static块了,因为JVM认为这是第二次加载类B了,所以static会在t1初始化时被忽略掉,所以直接初始化非static部分,也就是构造块部分(输出’‘构造块’‘)接着构造函数(无输出)。
(4)接着对t2进行初始化过程同t1相同(输出’构造块’),此时就对所有的static变量都完成了初始化,
(5)接着就执行static块部分(输出’静态块’),接着执行,main方法,同样也,new了对象,调用构造函数输出(‘构造块’)
10.
Java多线程实现方式主要有四种:
1、继承Thread类
2、实现Runnable接口
3、实现Callable接口通过FutureTask包装器来创建Thread线程
4、使用ExecutorService、Callable、Future实现有返回结果的多线程。
其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。
11.
Object类中的方法
1.getClass 2. hashCode 3. equals 4. clone 5. toString
6.notify 7. notifyAll 8. wait 9. finalize
synchronized Java关键字能够保证在同一时刻最多只有一个线程执行该段代码。
notify(): 是唤醒一个正在等待该对象的线程。
notifyAll(): 唤醒所有正在等待该对象的线程。
sleep 是Thread类中的方法 wait 和 sleep的区别:
wait指线程处于进入等待状态,形象地说明为“等待使用CPU”,此时线程不占用任何资源,不增加时间限制。
sleep指线程被调用时,占着CPU不工作,形象地说明为“占着CPU睡觉”,此时,系统的CPU部分资源被占用,其他线程无法进入,会增加时间限制。
11.
语句1,3,4,有错误
Java中的byte,short,char进行计算时都会提升为int类型。所以1,3,4得到一个int类型赋值给byte就不行。但是final修饰后byte不会转换成int计算,所以2得到的值还是byte
语句1错误:b3=(b1+b2);自动转为int,所以正确写法为b3=(byte)(b1+b2);或者将b3定义为int;
语句2正确:b6=b4+b5;b4、b5为final类型,不会自动提升,所以和的类型视左边变量类型而定;
语句3错误:b8=(b1+b4);虽然b4不会自动提升,但b1仍会自动提升,所以结果需要强转,b8=(byte)(b1+b4);
语句4错误:b7=(b2+b5); 同上。同时注意b7是final修饰,即只可赋值一次,便不可再改变。
12.
A,B 不能比父类中被重写的方法的访问权限更低,但是可以相同或高。
重写
参数列表必须完全与被重写方法的相同;
返回类型必须完全与被重写方法的返回类型相同;
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
13.
FFTT
String 创建和拼接
A:a指向堆内存,b指向常量池,因此地址不相等,false
B:java有常量优化机制,c也指向常量池,且与b指向同一个,
c选项String c = “my” + “String”;编译器编译的时候直接变成了String c=“myString”.
String a = new String("myString");
String b = "myString";
String c = "my" + "String";
String d = c;
System.out.print(a == b);//F 指向堆内存
System.out.print(a == c);//F 指向常量池
System.out.print(b == c);//T 字符串以非new形式创建时,会先在常量池寻找是否有相同字符;若有,则引用,若没有,则创建后引用。
System.out.print(b == d);//T
String a2 = "myString";
String c2 = "my" + new String("String");
System.out.println(a2==c2);//F 当用new的时候就是新创建一个
14.代码的执行结果
import java.util.*;
public class Demo {
public static void main(String[] args) {
Collection<?>[] collections =
{new HashSet<String>(), new ArrayList<String>(), new HashMap<String, String>().values()};//map.values()方法的返回值是Collection
Super subToSuper = new Sub();//静态类型是Super,实际类型是Sub
for(Collection<?> collection: collections) {//for循环出来他们实际上指的是collection对象表示的
System.out.println(subToSuper.getType(collection));//静态方法不存在重写,这里调用的是父类的getType方法,父类中有很多重载,根据数组的静态类型,确定调用版本
}
}
abstract static class Super {
public static String getType(Collection<?> collection) {
return "Super:collection";
}
public static String getType(List<?> list) {
return "Super:list";
}
public String getType(ArrayList<?> list) {
return "Super:arrayList";
}
public static String getType(Set<?> set) {
return "Super:set";
}
public String getType(HashSet<?> set) {
return "Super:hashSet";
}
}
static class Sub extends Super {
public static String getType(Collection<?> collection) {//
return "Sub"; }
}
}
Super:collection Super:collection Super:collection
static
考察点1:重载静态多分派——根据传入重载方法的参数类型,选择更加合适的一个重载方法
考察点2:static方法不能被子类重写,在子类中定义了和父类完全相同的static方法,则父类的static方法被隐藏,Son.staticmethod()或new Son().staticmethod()都是调用的子类的static方法,如果是Father.staticmethod()或者Father f = new Son(); f.staticmethod()调用的都是父类的static方法。
考察点3:此题如果都不是static方法,则最终的结果是A. 调用子类的getType,输出collection
Integer
Integer I = 128, 因为128超过了-128~127,i不会引用内存中的对象,会new一个新的对象。
i5和i6为100,在范围之内,在执行Integer i5=100时,就会直接缓存到内存中,当执行Integer i6=100时,就直接从缓存里取,而不需要新建对象
16.
运行时异常: 都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
非运行时异常 (编译异常): 是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
异常
Throwable: Java异常的顶级类,所有异常都继承于这个类。所有异常类的父类,是Object的直接子类。
Error:非程序异常,即程序不能捕获的异常,一般是编译或者系统性的错误。
Exception:程序异常类,由程序内部产生。
RuntimeException:运行时异常的特点是java编译器不会检查它,也就是说,当程序中可能出现这类异常时,会编译通过,但是在运行时会出现错误。
IOException:输入\输出异常。受查异常必须用try.catch 捕获。
异常的捕获顺序:先捕获子类,在捕获父类。
17.
public class Chinese {
private static Chinese objref =new Chinese();
private Chinese(){}//构造器私有化
public static Chinese getInstance() { return objref; }
}
public class TestChinese {
public static void main(String [] args) {
Chinese obj1 = Chinese.getInstance();
Chinese obj2 = Chinese.getInstance();
System.out.println(obj1 == obj2);
}
}
最后输出是true
单例模式
单例模式:某一个类将自己的构造方法定义成private,那么外部的程序就不能通过new的方法来实例化这个类的对象,这样是否实例化对象的责任就变成类本身自己的责任,而不是别人(其他程序)的责任。保证一个且仅有一个实例,并且提供一个访问它的全局访问点。
JAVA设计模式之单例模式详解
单例模式在产生时就已经写好了一个私有对象,被封装在一个方法里,通过该类点方法即可获取到这个对象,不管多少个引用,获取到的对象都是一样的,因此地址都一样
18.