Java笔记

以前都是以做acm题目学习语言的,不过感觉对很多概念没有整体性的认识。现在准备看书,然后看完书就去学习框架= =

不定期更新吧- -

ide: intellij idea(居然还有vim模式的。不过有段时间没用vim了 win上使用没感觉)

1

public/private/protected的具体区别

public 表示可以被其他包访问到。
private表示只有这个类能访问。。
protected表示可以被儿子的类访问。 (然而貌似好像也可以被本包的类访问?)


2

抽象类。拿来写接口or扩展用的?可以new一个非抽象类儿子对象。然后方法在儿子对象里面写,不过自己本身也要定义那个方法。有抽象方法的都定义成抽象类。好吧,并不是用来写接口的。抽象类无法实例化。抽象类的子类如果没实现抽象类的所有抽象方法,则子类必须定义为抽象类,直到所有爸爸的抽象方法被实现。


3

final 这玩意好像是不可变的,比如String类。final类的方法默认为final。这个类不可被覆盖,不能被继承。简单来说就是不用修改的东西。不过final类里面的属性(域)并不是final类型的。


4

static 被这玩意修饰的方法不需要对象来调用。。静态方法只能访问静态方法。非静态可以访问静态。静态变量是被所有对象共享的。。相当于只加载一次这样。第一次new一个对象的时候就加载好了。不允许拿来修饰局部变量。static块会被加载一次,但不能出现在方法内部中。还有一个作用。就是静态导包,,相当于可以简化一些操作。= =


5

儿子初始化的时候要用到父亲类的东西可以用super。


6
ArrayList,,这玩意就是c++的vector。。有装箱之类的功能,就是隐式转换。。。反正就是直接用。


7

父亲 xx=new 儿子()的时候注意,只能用父亲类的方法。父亲可以引用自己的对象,也可以引用儿子的对象。但是方法是儿子可以用父亲的,父亲只能用自己的。


8
反射,感觉还是蛮复杂的东西。差不都就是对于任意一个对象都可以知道它的方法和属性,Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类。这样的特点就是反射。能获取一个类的访问修饰符,方法,成员,构造方法,父亲的信息。通俗就是获得在运行的时候的才知道的类的方法 成员啥的,,就叫做反射吧= =?

9
多态这玩意就是子类方法重写嘛。= =感觉没什么毛病 认真看了两天java的书,,目前没什么压力啊= =。?

10
接口,不能new它,,但是可以声明变量,比如 接口 x。然后x就可以new一个实现了这个接口的类。interface是声明接口的,默认都是public,可以不加public。但是接口里面的方法必须要加上public,不然会暴露。。= =接口也是可以扩展的,类似继承。接口不能有实例域,但是可以有常量,类型都是public static final。一个类只能有一个爸爸,然而可以实现多个接口,用implement x1,x2,x3的形式。接口本身也是可以有多个爸爸的。接口方法都是public abstract的。,不过接口里面的方法都不用实现,要在引用这个接口的类里面具体实现。总的来说,接口就是为了一个方法多次出现而设计的吧?然后具体类要用的时候可以直接调用这个接口,然后类里面写接口方法就行了。并且可以多个,这点抽象类做不到。因为一个类只能有一个类爸爸。
 和抽象类的不同:
1抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
2抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
3接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
4一个类只能继承一个抽象类,而一个类却可以实现多个接口。

11
克隆方法clone,引用的不是同一个对象。。clone过来的不会改变原来的值,不过要是用直接赋值的话,是同一个对象,修改的话会改变原来那个东东。。

upd 11/9

12
Java内部类大概有4种吧。
1普通内部类
2方法内部类
3匿名内部类
4静态内部类

1普通内部类可以拥有访问他爸爸的成员和方法。并且可以访问private级别的。普通内部类不能直接创建实例,要先创建它外面的类。
访问的例子
public class OuterClass {
    private String str;
    
    public void outerDisplay(){
        System.out.println("outerClass...");
    }
    
    public class InnerClass{
        public void innerDisplay(){
            //使用外围内的属性
            str = "chenssy...";
            System.out.println(str);
            //使用外围内的方法
            outerDisplay();
        }
    }
    
    /*推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 */
    public InnerClass getInnerClass(){
        return new InnerClass();
    }
    
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        OuterClass.InnerClass inner = outer.getInnerClass();
        inner.innerDisplay();
    }
}
--------------------
chenssy...
outerClass...


2方法内部类只能在方法当中定义,方法执行完毕立即从内存中删掉。如果需要使用方法中的局部变量,要把这个局部变量定义成final。(Java8中没有必要了)
3匿名内部类必须要继承一个父类或者实现一个接口。并且匿名内部类只能用一次。因为抽象类和接口不能实例化,所以可以写一发来实现他们的方法。这样代码可以更加的简洁。反正匿名内部类一定要继承爸爸或者实现一个接口。。
简单的例子。因为Person是抽象类。Person没有办法直接实例化,所以写了一发匿名内部类。这样可以更加的简洁。
abstract class Person {
    public abstract void eat();
}
 
public class Demo {
    public static void main(String[] args) {
        Person p = new Person() {
            public void eat() {
                System.out.println("eat something");
            }
        };
        p.eat();
    }
}
4
静态内部类和和普通内部类不同的一点就是,静态内部类只能用爸爸类的静态成员和静态方法。
总结:
使用内部来类的最重要的就是可以实现多继承,体现了Java强大的多态。并且彼此之间不影响,但是有“微妙”的联系。

upd 11/10

13代理
代理就是扩展目标对象的功能。有静态代理和动态代理。
1静态代理 差不多就是弄一个类,然后搞两个类分别实现这个父类。一个是目标对象,一个是代理对象。目标对象和代理对象的方法要相同。示例
// 抽象角色:
abstract public class Subject {
    abstract public void  request();
}

// 真实角色:实现了Subject的request()方法
public class  RealSubject  extends  Subject  {
  public  RealSubject()  { }

  public void  request()  {
     System.out.println( " From real subject. " );
    }
}

// 代理角色:
public class  ProxySubject  extends  Subject  {
  // 以真实角色作为代理角色的属性
  private  Subject realSubject;

  public  ProxySubject(Subject realSubject)  {this.realSubject = realSubject }

  // 该方法封装了真实对象的request方法
  public void  request()  {
     preRequest();
     realSubject.request();  // 此处执行真实对象的request方法
     postRequest();
  }
  ...
}

// 客户端调用:
RealSubject real = new RealSubject();
Subject sub = new  ProxySubject(real);
Sub.request();

这样做就是有个缺点,,如果大量代理会出现大量的类,每个类都要代理一个目标对象。可以通过动态代理解决。
2动态代理
先给个示例。


// 抽象角色(之前是抽象类,此处应改为接口):
public  interface Subject {
  abstract  public  void request();
}

// 具体角色RealSubject:
public  class RealSubject implements Subject {
  public RealSubject() {}

  public  void request() {
    System.out.println( " From real subject. " );
 }

}

// 代理处理器:
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;

public  class DynamicSubject implements InvocationHandler {
  private Object sub;
  public DynamicSubject() {}

  public DynamicSubject(Object obj) {
    sub = obj;
  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println( " before calling "  + method);
    method.invoke(sub,args);

    System.out.println( " after calling "  + method);
    return  null ;
  }
}



// 客户端:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Client {

  static public void main(String[] args) throws Throwable {
   RealSubject rs = new RealSubject(); // 在这里指定被代理类
   InvocationHandler ds = new DynamicSubject(rs);
   Class cls = rs.getClass();

   // 以下是一次性生成代理
   Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),ds );
   subject.request();
  }
}

// 程序运行结果:
before calling public abstract void Subject.request()
From real subject.
after calling public abstract void Subject.request()
再看一个例子。

import java.lang.reflect.Proxy;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.InvocationTargetException;

//接口
interface Fruit{
     public abstract void addFruit();
 }

//实现类
class FruitImpl implements Fruit{
     @Override
     public void addFruit(){
         System.out.println("添加水果...");
     }
 }

//测试类---编写代理,增强实现类中的方法
public class FruitDemo{
     public static void main(String[] args){
         //创建动态代理对象
        Object f = Proxy.newProxyInstance(FruitImpl.class.getClassLoader(), FruitImpl.class.getInterfaces(),
             new InvocationHandler(){
                 @Override
                 public Object invoke(Object Proxy, Method method, Object[] args){
                     System.out.println("选择水果.....................");
                     Object obj = null;
                         try{
                             obj = method.invoke(new FruitImpl(),args);
                         }catch(IllegalAccessException | InvocationTargetException | IllegalArgumentException e){
                             e.printStackTrace();
                         }
                     System.out.println("添加成功~~");
                     return obj;
                 }
             }
         );

        //代理对象向下(接口)转型
        Fruit f1 = (Fruit) f;

        //转型后的对象执行原方法(已增强)
         f1.addFruit();
     }
 }


以上代码可以看出,动态代理直接封装了Object,这样就灵活了许多,可以对任意的实现接口类进行功能扩展。但是动态代理只能代理接口。
这玩意的原理还是蛮复杂的。。必须要去看Proxy和newproxyinstance的源码。(先留个坑)invoke还是有用到反射机制的,第二个例子貌似用到了匿名函数,对InvocationHandler接口重写。以后学完源码来试着说说原理吧。

14 java泛型
泛型差不多就是将原来的参数类型范围给扩大了,就是所有的类型都可以传入进去。逻辑上可以看成不同的类型了,但本质上是同一种类型= =。。Java泛型没有泛型数组的说法。类型通配上限符和类型通配下限符,前面一个要求是那个类的儿子,后面一个要求是那个类的爸爸。形如:Class<? extends babaClass>  ?为儿子类和Class<?  super erziClass>  ?为爸爸类。

upd 2017/11/13

15 Serializable的作用,实现Serializable接口的对象可以序列化。因为对象的存在周期不可能比jvm长,所以序列化就是为了在jvm结束时仍然可以获得对象的状态。直接保存起来,以后也是可以直接拿来读取的,

16 transient是用来防止被序列化的东西,只能用来修饰字段。volatile修饰的成员变量就是每次被线程访问时,强迫从内存里面读取,然后改变时,强迫写回内存。这样任何时刻,两个不同的线程访问的都是同一个成员变量值。

17 多线程


这张图是java线程的生命周期。


upd  2017/11/14


调用多线程的最简单的方法就是继续Thread类,重写run方法。。然后稍微看了下Thread的源码,发现是实现了一个Runnable接口,里面只有一个public abstract void run()方法。此时start就显然调用的就是重写的那个run方法。要是直接调用run方法的话,,显然是主线程的那个,也就是和普通方法没有任何不同。线程的应该是C代码实现的吧,没看见run里面有任何内容。线程顺序和本地的操作系统 cpu有关。
任何线程实现都要调用Thread里面的start方法。
还有一种方式就是实现Runnable接口,重写方法run。但是也要变成Thread的一个实例才能启动线程。
jdk源码是这样的。
public void run() {  
  if (target != null) {  
   target.run();  
  }  
}  

差不都就是相当于传入了一个target对象,此时调用的就是这个类的run。
例子
public class MyThread extends OtherClass implements Runnable {  
  public void run() {  
   System.out.println("MyThread.run()");  
  }  
}  



MyThread myThread = new MyThread();  
Thread thread = new Thread(myThread);  
thread.start();  
这是第二种创建线程的方式。


实现Callalbe通过FutureTask包装器来创建Thread线程。
Callable接口以及实现和调用。
public interface Callable<V>   { 
  V call() throws Exception;   } 


public class SomeCallable<V> extends OtherClass implements Callable<V> {

    @Override
    public V call() throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

}


Callable<V> oneCallable = new SomeCallable<V>();   
//由Callable<Integer>创建一个FutureTask<Integer>对象:   
FutureTask<V> oneTask = new FutureTask<V>(oneCallable);   
//注释:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。 
  //由FutureTask<Integer>创建一个Thread对象:   
Thread oneThread = new Thread(oneTask);   
oneThread.start();   
//至此,一个线程就创建完成了。

这是第三种方式。。
/**
 * 
 */
package com.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author Maggie
 *
 */
public class ThreadPool 
{
	/* POOL_NUM */
	private static int POOL_NUM = 10;
	
	/**
	 * Main function
	 */
	public static void main(String[] args)
	{
		ExecutorService executorService = Executors.newFixedThreadPool(5);
		for(int i = 0; i<POOL_NUM; i++)
		{
			RunnableThread thread = new RunnableThread();
			executorService.execute(thread);
		}
	}
}

class RunnableThread implements Runnable
{
	private int THREAD_NUM = 10;
	public void run()
	{
		for(int i = 0; i<THREAD_NUM; i++)
		{
			System.out.println("线程" + Thread.currentThread() + " " + i);
		} 
	}
}


第四种,这个还是有点烦的。。就是通过线程池,,,代码上面的5说明创建了有5个线程的线程池。。具体的excute方法还没看过 以后补坑吧。。

下面说说一些常用函数。
join函数,等待子线程终止后,才能运行后面的主程。
sleep函数,,就是停止多少毫秒。。
yield函数,暂停当前正在执行的线程对象,并且执行其他的线程。
setPriority函数,看上去就是用来搞优先级的。
MIN_PRIOIRTY=1
NORM_PRIOIRTY=5
MAX_PRIORITY=10  
直接设置就行
interrupt函数,让线程异常时中断,但是你要是吃掉了这个异常,则不会中断。

wait方法和notify方法必须和synchronized一起使用。synchronized这玩意相当于一个厕所的样子,保证在synchronized块里面执行的代码不会用其他线程来干扰,有的话就暂时阻塞掉。并且进入synchronized里面的代码会获得一个对象锁。wait方法差不多就是把这个锁拿走,然后本线程休眠。。notify就是把这个锁拿来。。执行完这个块后,唤醒这个线程。注意并不是立即唤醒,而是当这个块执行完后才唤醒。一个经典轮流打印ABC的两种不同的示例代码


public class PrintABC {  
  
    public static Boolean isThreadA = true;  
    public static Boolean isThreadB = false;  
    public static Boolean isThreadC = false;  
  
    public static void main(String[] args) {  
        final PrintABC abc = new PrintABC();  
        new Thread(new Runnable() {  
            public void run() {  
                for (int i = 0; i < 10; i++) {  
                    synchronized (abc) {  
                        while(!isThreadA) {  
                            try {  
                                abc.wait();  
                            } catch (InterruptedException e) {  
                                // TODO Auto-generated catch block  
                                e.printStackTrace();  
                            }  
                        }  
                            System.out.print("A");  
                            isThreadA = false;  
                            isThreadB = true;  
                            isThreadC = false;  
                            abc.notifyAll();  
                    }  
                }  
            }  
        }).start();  
  
        new Thread(new Runnable() {  
            public void run() {  
                for (int i = 0; i < 10; i++) {  
                    synchronized (abc) {  
                        while(!isThreadB) {  
                            try {  
                                abc.wait();  
                            } catch (InterruptedException e) {  
                                // TODO Auto-generated catch block  
                                e.printStackTrace();  
                            }  
                        }  
                            System.out.print("B");  
                            isThreadA = false;  
                            isThreadB = false;  
                            isThreadC = true;  
                            abc.notifyAll();  
                    }  
                }  
            }  
        }).start();  
          
        new Thread(new Runnable() {  
            public void run() {  
                for (int i = 0; i < 10; i++) {  
                    synchronized (abc) {  
                        while(!isThreadC) {  
                            try {  
                                abc.wait();  
                            } catch (InterruptedException e) {  
                                // TODO Auto-generated catch block  
                                e.printStackTrace();  
                            }  
                        }  
                            System.out.print("C");  
                            isThreadA = true;  
                            isThreadB = false;  
                            isThreadC = false;  
                            abc.notifyAll();  
                    }  
                }  
            }  
        }).start();  
    }  
}
这是第一种,明显的。A打印时,用while循环让BC释放掉自己的锁,此时A打印时BC是为阻塞状态的。其他两个也一样。为什么要用while呢,就是为了保证每一次唤醒都执行了这个条件。。如果用if的话,,可能会产生玄学bug
第二种

package com.multithread.wait;
public class MyThreadPrinter2 implements Runnable {   
	  
    private String name;   
    private Object prev;   
    private Object self;   
  
    private MyThreadPrinter2(String name, Object prev, Object self) {   
        this.name = name;   
        this.prev = prev;   
        this.self = self;   
    }   
  
    @Override  
    public void run() {   
        int count = 10;   
        while (count > 0) {   
            synchronized (prev) {   
                synchronized (self) {   
                    System.out.print(name);   
                    count--;  
                    
                    self.notify();   
                }   
                try {   
                    prev.wait();   
                } catch (InterruptedException e) {   
                    e.printStackTrace();   
                }   
            }   
  
        }   
    }   
  
    public static void main(String[] args) throws Exception {   
        Object a = new Object();   
        Object b = new Object();   
        Object c = new Object();   
        MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);   
        MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);   
        MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);   
           
           
        new Thread(pa).start();
        Thread.sleep(100);  //确保按顺序A、B、C执行
        new Thread(pb).start();
        Thread.sleep(100);  
        new Thread(pc).start();   
        Thread.sleep(100);  
        }   
}  



这个第二种方法就是利用sleep,刚好是前一个被锁,然后A打完后。B在sleep的时间里面就被唤醒了。进入B线程 ,把A锁掉。打印B。然后进入C,B锁掉。打印C。锁掉B。此时A还是被锁的状态。。所以刚好轮流打印。

upd 2017/11/15

感觉写的太长了。。有必要再开新的篇章了。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值