孙卫琴:java面向对象编程,从头看起。
1.
执行beibei.speak()的方法时。jvm根据局部变量beibei的引用找到堆区的Doll的实例,再根据Dell实例引用的方法的引用,定位到方法
区中Doll类的信息,从而获得speak方法的字节码,接着执行speak()方法
2、成员变量,不管是否代码初始化,会默认给初始化的值。
byte int short int long初始为0.对象的初始null
局部变量定义时不初始化,使用时会报错。编译不通过。
如果只定义不使用,可以编译通过。这样代码也没意义。
3、计算时,数字类型会默认强转,可理解为向上强转。。
byte char short int long float 跟 double 计算>>double
byte char short int long跟 float计算>>float
byte char short int跟 long>> long
byte char short跟 int >> int
byte char short 之间预算 都 >>int (自己定义为"小int"型: byte char short int,会转为int)
X++此类运算符是不会强转的。
inti=5/2.0;//会报错。默认转为double.
4、switch只能接收四种类型("小int"型byte char short int)。
5、位运算符 & 都为1时为1
| 一个为1既为1
^异或 相异为1
>>右移 左边补充符号位(正数0负数1) a>>b 相当于 a/2b%32
eg: 12>>1 = 12/21
12>> 34 = =12/22
6、== equals
== 是引用类型时,两个引用变量必须引用同一个对象才为true ,两个值必须是同类型的(继承类也可),否则编译不通过。
equals:当参数obj引用的对象与当前对象为同一对象时。才为true。
看到上面这我迷糊了。==和equals不是一样吗。看父类Object方法。就是一样的。困扰我们的是那些重载了equals的类。
public boolean equals(Object obj) {
return (this == obj);
}
很多类都重写了equals方法如:String、Integer.这些类比较值时用equals。
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
Integer:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
总结:我们比较2个对象时。用==和equals是一样的。只是比较某些对象的值。像String、Integer、Long等时用equals.
是因为这些类都重写了equals方法.
7、方法重载overload(父类和子类之间):
7.1.方法名相同
7.2.参数的个数 类型顺序至少有1个不同
7.3.返回值可以不同,修饰符可以不同,只返回值不同,报错。
方法重写override:
7.4.方法名称 参数 返回类型必须和父类完全一致,
7.5.子类不能缩小父类方法的访问权限。
7.5不能比父类抛出更多的异常
7.6.静态方法也可重写。子类也必须定义为static的
//public class Child extend Parent
如父类Parent有2个方法 methordA()和static 的methordB(),子类Child将A、B都重写。
Parent p =new Child();
//p.methordA()执行子类的,p.methordB()实际执行为父类的
Childp =new Child();//都是执行子类的。
另:成员变量为静态绑定。如上。如果Parent中定义String var ="aaa";Child中定义 String var ="bbb";
Parent p =new Child(); p.var的值为"aaa"。
7.6父类的private方法不能被覆盖
8、抽象类、接口:
8.1.定义抽象方法的类不能实现该方法。包含抽象方法的类,必须定义为抽象类。但是抽象类可以不包含抽象方法
8.2.构造方法 静态方法不能定义为抽象的
8.3.抽象类不可以new 但是可引用子类的实例 Parent p = new Child()
8.4.抽象方法或类不能用final修饰,子类要实现方法。
8.5.接口中的成员变量默认为public static final的。方法默认是public abstract
9.static修饰。静态变量:在内存中只有一块地址,所有类的实例公用。
public static int a=0;
public static void main(String[] args) {
TestObject object1 = new TestObject();
TestObject object2 = new TestObject();
object1.a++;
System.out.println(object1.a);//打印1
System.out.println(object2.a);//打印1
System.out.println(TestObject.a);//打印1
}
final static的变量定义时初始化,引用的时候,jvm编译成的class会直接赋值为常量。
如:Test中定义public static final int MAXT=999; Test2中引用 int a=Test.MAXT
实际class文件编译为int a=999;项目上线后要修改MAXT=888,Test2也要重新编译,只上传Test.class无效。
10、多线程
10.1线程实现方式:实现Runnable接口或者继承Thread类
//实现Runnable接口
public class MyThread implements Runnable {
public static void main(String args[]) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt, "t1");
t1.start();
}
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName());
}
}
}
//继承Thread类
public class TestThread extends Thread {
@Override
public void run() {
super.run();
System.out.println( currentThread().getName());//打印Thread-0
}
@Override
public synchronized void start() {
super.start();
}
public static void main(String[] args) {
TestThread testThread1 = new TestThread();
testThread1.start();
}
}
10.2 以TestTread类代码为例。线程的启动。主线程main.调用testThread1.start().时会调用testThread1的run()方法去启动线程。
执行run()方法的线程为testThread1。
如果覆盖start方法。
@Override
public synchronized void start() {
//super.start();
run();
}
此时执行方法的线程一直是main线程。这成了普通的方法调用。run方法中会打印main
10.3一个线程只能启动一次。调用2次start()时会报错。
10.4线程状态。
New (新建):创建后
Runnable:调用start()方法后。位于可运行池中,等待获得CPU使用权
Running:占用CPU,执行代码。一个CPU。任何时刻只能运行一个线程。
Blocked:某些状态导致放弃CPU。直到CPU使用权才会重新进入Runnable状态。
阻塞可分3种:1.对象等待池中(执行了对象的wait()方法)2.对象锁池中(读取某个对象的同步锁。锁被其他对象占用时,会等待,放到锁池中)
3.其他阻塞:sleep 其他线程的join()、或者发出了I/O请求等
Dead:退出run()方法。Thread类的isAlive()方法可判断是否活着。
10.5.后台进程设置,setDaemon(true),先set再调用start()启动,启动后再调用setDaemon(true)会报错。其他进程结束时,才会结束。
public class TestThread extends Thread {
int sharea=0;
public void run(){
while(true){
System.out.println("sharea="+sharea++);;
if(sharea==100)break;
yield();
}
}
@Override
public synchronized void start() {
super.start();
Thread thread = new Thread(){
public void run(){
while(true){
sharea=0;
System.out.println(" setDaemon;"+ this.getClass());
try {
sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
thread.setDaemon(true);
thread.start();
}
public static void main(String[] args) {
TestThread testThread1 = new TestThread();
TestThread testThread2 = new TestThread();
testThread1.start();
testThread2.start();
}
}
10.6.多个线程修改共享数据时。可能需要同步。下面的代码把共享的sharea+i.再-i.结果应该一直是1。
但是多个线程时可能会出问题。
public class MyThread implements Runnable {
int sharea=1;
public void run(){
for(int i=0;i<100;i++){
sharea=sharea+i;
System.out.println(Thread.currentThread().getName()+"++++sharea="+sharea);;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
sharea=sharea-i;
System.out.println(Thread.currentThread().getName()+"----sharea="+sharea);;
}
}
public static void main(String args[]) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
t1.start();
t2.start();
}
}
未避免这种情况可以在方法前加synchronized修饰。t1.启动后会执行run方法。加了此修饰。执行完后t2才会执行。
synchronized 2种写法是等同的:
public synchronized void methodName(){ /*.........*/}
public void methodName(){ synchronized (this){/*.........*/} }
10.7ThreadLocal类。存放线程的局部变量。每个线程不同。
如一个线程的run方法中要获得一类单例类。
public synchronized void run(){
GloalConfig config = GloalConfig.getInstance();
//config......
}
正常。 启动多个线程时。获得的config类都是同一个对象。
但是用下面的代码。每个线程获得的config都是不同的对象。
public class GloalConfig {
private static final ThreadLocal<GloalConfig> config = new ThreadLocal<GloalConfig>();
public static GloalConfig getInstance(){
GloalConfig gloalConfig = config.get();
if(gloalConfig==null){
gloalConfig = new GloalConfig();
config.set(gloalConfig);
}
return gloalConfig;
}
}
11.数组。
11.1多维的要按顺序初始化。int a[2][][3] 错误。
11.2回顾经典的冒泡、二叉查找。
public static void main(String[] args) {
//冒泡排序
int [] arr=new int[]{34,7,8,4,5,67,34,23,1,2,34,56,43,8,7,5};
for(int i =0;i<arr.length-1;i++){
for(int j=0;j<arr.length-i-1;j++){
if(arr[j]>arr[j+1]){
int temp =arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
}
//二叉查找
int [] arr2=new int[]{1,2,4,5,6,7,8,10,13,23,34,35,41,43,44,56};
int value=49;
System.out.println(getIndex(arr2,value));
}
public static int getIndex(int [] array ,int value){
int low=0;
int high=array.length-1;
int middle=0;
while(low<=high){
middle=(low+high)/2;
if (value==array[middle]){
return middle;
}
if(value<array[middle]){
high=middle-1;
}else{
low=middle+1;
}
}
return -1;
}
11.3数组哈希表。hashMap的实现里用到。看一天hashMap的代码。
重要的put 和addEntry方法
//默认的构造,数组长度16。容载因子0.75.
public MyHashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
//增加值
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
System.out.println(key+"- table["+i+"]:"+ table[i]);
int j=1;
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
System.out.println(key+"-key hash:"+hash+",table["+i+"]:"+e.key+",jj="+j++);
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
System.out.println(key+"-已经存在的");
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}else{
System.out.println(key+"-不存在的 ,数组索引对象已存在,需要加到链中");
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
//增加链值
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> next = table[bucketIndex];//旧的Entry值作为新值的next
table[bucketIndex] = new Entry<K,V>(hash, key, value, next);
System.out.println(key+"-put key:"+key.toString()+",table i:"+bucketIndex);
if (size++ >= threshold)//长度达到 length*容载因子时扩展
resize(2 * table.length);
}
//单独为null key处理
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
12.泛型使用
ArrayList<GenTester> list = new ArrayList<GenTester>();
//list.add.GenTester...
GenTester<String> genTester=list.get(0);//编译时class文件出增加强制类型转换
System.out.println(genTester.getFirst());
System.out.println(genTester.getSecond());
反编译class文件:
ArrayList list = new ArrayList();
GenTester genTester = (GenTester)list.get(0);
System.out.println((String)genTester.getFirst());
System.out.println((String)genTester.getSecond());
12.1泛型不能用基本类型Test<double>//错误,Test<Double>//正确
12.2比较时注意
GenTester<Integer> genTester = new GenTester<Integer>();
GenTester<String> genTester1 = new GenTester<String>();
genTester.getClass()//GenTester
genTester1.getClass()//GenTester
都是GenTester.class
12.3 数组声明时禁止指定类型
GenTester<String> [] genTesters = new GenTester<String>[2];//错误
GenTester [] genTesters = new GenTester[2];//正确
因为:
genTesters[0]= new GenTester<Integer>();
genTesters[1]= new GenTester<Date>();
即使数组定义时制定泛型为String.但此2句符合数组的校验,编译应该通过。这样使用时就会产生强制类型转换错误。
为了防止,禁止声明时指定类型