public/private/protected的具体区别
public 表示可以被其他包访问到。
private表示只有这个类能访问。。
抽象类。拿来写接口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
感觉写的太长了。。有必要再开新的篇章了。