java面向对象编程学习笔记

孙卫琴: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

注意:jvm运行时会把静态方法和所属的类绑定,把实例方法和所属的实例绑定。

如父类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句符合数组的校验,编译应该通过。这样使用时就会产生强制类型转换错误。
为了防止,禁止声明时指定类型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值