第二章:对象及变量的并发访问

为什么要使用多线程编程?什么时候会出现线程安全问题?

在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的的资源:一个变量、一个对象、一个文件、一个数据库表等,而当多个线程同时访问同一个资源的时候,就会存在一个问题:

  由于每个线程执行的过程是不可控的,所以很可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错。

举个简单的例子:

  现在有两个线程分别从网络上读取数据,然后插入一张数据库表中,要求不能插入重复的数据。

  那么必然在插入数据的过程中存在两个操作:

  1)检查数据库中是否存在该条数据;

  2)如果存在,则不插入;如果不存在,则插入到数据库中。

  假如两个线程分别用thread-1和thread-2表示,某一时刻,thread-1和thread-2都读取到了数据X,那么可能会发生这种情况:

  thread-1去检查数据库中是否存在数据X,然后thread-2也接着去检查数据库中是否存在数据X。

  结果两个线程检查的结果都是数据库中不存在数据X,那么两个线程都分别将数据X插入数据库表当中。

  这个就是线程安全问题,即多个线程同时访问一个资源时,会导致程序运行结果并不是想看到的结果。

本章主讲synchronized同步关键字使用,让我们实现线程安全的程序,解决非线程安全问题。

本章目录:

1.1.内部变量不存在线程安全问题

1.2.全局变量导致非线程安全

1.3.使用synchronized进行同步

1.4.多个对象多个锁

1.5.脏读

1.6.将任意对象作为对象监视器

1.7.同步synchronized方法无限等待与解决

1.8.多线程死锁

1.9.死循环

2.0.使用关键字volatile更新数据

2.1.使用原子类进行修改操作

2.2.原子类也并不完全安全

1.1.内部变量不存在线程安全问题

当多个线程执行一个方法,方法内部的局部变量并不是临界资源,因为方法是在栈上执行的,而Java栈是线程私有的,因此不会产生线程安全问题。

class PrivateObject {

	public void add(String username) {
		int num = 0;
		if ("a".equals(username)) {
			num = 100;
			System.out.println("a set over");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} else {
			num = 200;
			System.out.println("b set over");
		}
		System.out.println(username + " num = " + num);
	}

}
class ThreadA extends Thread{
	private PrivateObject object;

	public ThreadA(PrivateObject object) {
		super();
		this.object = object;
	}

	@Override
	public void run() {
		super.run();
		object.add("a");
	}

}
class ThreadB extends Thread{
	private PrivateObject object;

	public ThreadB(PrivateObject object) {
		super();
		this.object = object;
	}

	@Override
	public void run() {
		super.run();
		object.add("b");
	}

}
public static void main(String[] args) {
	PrivateObject privateObject = new PrivateObject();
	ThreadA threadA = new ThreadA(privateObject);
	threadA.start();
	ThreadB threadB = new ThreadB(privateObject);
	threadB.start();
}
运行结果:
a set over
b set over
b num = 200
a num = 100

很明显,num的值是正确的,如果ThreadA线程先抢了资源了那么执行的时候就是num = 100然后停了2秒后输出num的值,然后ThreadB线程执行,设置了num=200,那么输出来的值就是200了。

然而我们把int num = 0;放到全局变量的话就会变成1.2.

1.2.全局变量导致非线程安全

该例子把上面的对象修改一下:

class PrivateObject {
	int num = 0;
	public void add(String username) {
		if ("a".equals(username)) {
			num = 100;
			System.out.println("a set over");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} else {
			num = 200;
			System.out.println("b set over");
		}
		System.out.println(username + " num = " + num);
	}

}
运行结果:
a set over
b set over
b num = 200
a num = 200

假设ThreadA线程先被执行,那么全局变量num = 100,然后进行一个耗时操作,此时,ThreadB进入执行了更改了num的值编程200然后输出,此时num = 200,就在这个时候ThreadA耗时已经完成,然后输出他的值,发现输出来是 num = 200,因为这个时候num已经被ThreadB修改了,所以输出了 num = 200。那么如何解决非线程安全的问题呢?我们来看看1.3.

1.3.使用synchronized进行同步

public synchronized void add(String username) {
	if ("a".equals(username)) {
		num = 100;
		System.out.println("a set over");
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	} else {
		num = 200;
		System.out.println("b set over");
	}
	System.out.println(username + " num = " + num);
}
在该函数添加了synchronized之后你会发现输出的结果:
a set over
a num = 100
b set over
b num = 200

这个实例输出来的结果可以看出它是线程安全的。由于该实例是同步的,所以我们看到的结果如果是ThreadA先执行的话就会造成阻塞,后面进行等待。这种结果一样不是我们想要的,那么如何解决这种问题呢?我们来看1.4.

1.4.多个对象多个锁

public static void main(String[] args) {
	PrivateObject privateObject1 = new PrivateObject();
	PrivateObject privateObject2 = new PrivateObject();
	ThreadA threadA = new ThreadA(privateObject1);
	threadA.start();
	ThreadB threadB = new ThreadB(privateObject2);
	threadB.start();
}
创建多个实例让线程执行分成多路,这就是异步处理,我们可以看出不管ThreadA是否执行了耗时ThreadB还是会去执行,由于多个实例执行多个同步方法。结果输出:
a set over
b set over
b num = 200
a num = 100

1.5.脏读

有时候就算你加了同步还是会出现数据脏读现象。例如:

class Service {
	private String name = "A";
	private String password = "AA";
	public synchronized void update(String name, String password){
		try {
			System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "进入同步");
			this.name = name;
			Thread.sleep(3000);
			this.password = password;
			System.out.println("name = " + name + ", password = " + password);
			System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "离开同步");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public String select(){
		return "name = " + name + ", password = " + password;
	}

}

class MyThread extends Thread {

	Service service;

	public MyThread(Service service) {
		super();
		this.service = service;
	}

	@Override
	public void run() {
		super.run();
		service.update("B", "BB");
	}

}
public static void main(String[] args) {
	Service service = new Service();
	MyThread myThread = new MyThread(service);
	myThread.start();
	try {
		Thread.sleep(200);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
	System.out.println(service.select());
}
结果:
当前线程为:Thread-0 在 1467706088809进入同步
name = B, password = AA
name = B, password = BB
当前线程为:Thread-0 在 1467706091811离开同步

解决当前问题只能在读取的时候再加上对象锁,就不会有这样的情况了

public synchronized String select(){
	return "name = " + name + ", password = " + password;
}
结果:
当前线程为:Thread-0 在 1467706347098进入同步
name = B, password = BB
当前线程为:Thread-0 在 1467706350105离开同步
name = B, password = BB

1.6.将任意对象作为对象监视器

class Service {
	String string = new String();
	public void update(){
		synchronized(string){
			try {
				System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "进入同步");
				Thread.sleep(3000);
				System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "离开同步");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}
public static void main(String[] args) {
	Service service = new Service();
	MyThread myThreadA = new MyThread(service);
	myThreadA.start();
	MyThread myThreadB = new MyThread(service);
	myThreadB.start();
}
输出结果:
当前线程为:Thread-0 在 1467707013916进入同步
当前线程为:Thread-0 在 1467707016925离开同步
当前线程为:Thread-1 在 1467707016925进入同步
当前线程为:Thread-1 在 1467707019926离开同步

锁非this对象具有一定的优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会收到阻塞,所以影响运行效率;但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其它锁this同步方法争抢this锁,则可大大提高运行效率。

1.7.同步synchronized方法无限等待与解决

同步方法容易造成死循环。

class Service {
	public synchronized void methodA(){
		System.out.println("methodA begin");
		boolean isContinueRun = true;
		while(isContinueRun){

		}
		System.out.println("methodA end");
	}

	public synchronized void methodB(){
		System.out.println("methodB begin");
		System.out.println("methodB end");
	}

}

class ThreadA extends Thread {
	private Service service;

	public ThreadA(Service service) {
		this.service = service;
	}

	@Override
	public void run() {
		super.run();
		service.methodA();
	}

}

class ThreadB extends Thread {
private Service service;

	public ThreadB(Service service) {
		this.service = service;
	}

	@Override
	public void run() {
		super.run();
		service.methodB();
	}

}
public static void main(String[] args) {
	Service service = new Service();
	ThreadA threadA = new ThreadA(service);
	threadA.start();
	ThreadB threadB = new ThreadB(service);
	threadB.start();
}
运行结果是死循环:
methodA begin

那么如何解决死循环呢?那就是不要使用同一对象监视器,也就是使用异步处理线程

class Service {
	String a = "a";
	public void methodA(){
		synchronized (a) {
			System.out.println("methodA begin");
			boolean isContinueRun = true;
			while(isContinueRun){

			}
			System.out.println("methodA end");
		}
	}

	String b = "b";
	public void methodB(){
		synchronized (b) {
			System.out.println("methodB begin");
			System.out.println("methodB end");
		}
	}

}
这样就不会造成死锁了。
methodA begin
methodB begin
methodB end

1.8.多线程死锁

不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成。下面我举例说明:

class DealThread implements Runnable{
	public String username;
	public Object lock1 = new Object();
	public Object lock2 = new Object();

	public void setFlag(String username) {
		this.username = username;
	}

	@Override
	public void run() {
		if("a".equals(username)){
			synchronized (lock1) {
				System.out.println("username = " + username);
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("a-----");
				synchronized (lock2) {
					System.out.println("按lock1 -> lock2代码顺序执行了");
				}
			}
		}
		if("b".equals(username)){
			synchronized (lock2) {
				System.out.println("username = " + username);
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("b-----");
				synchronized (lock1) {
					System.out.println("按lock2 -> lock1代码顺序执行了");
				}
			}
		}
	}

}
public static void main(String[] args) {
	DealThread t1 = new DealThread();
	t1.setFlag("a");
	Thread thread1 = new Thread(t1);
	thread1.start();
	try {
		Thread.sleep(100);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
	t1.setFlag("b");
	Thread thread2 = new Thread(t1);
	thread2.start();
}
运行结果:
username = a
username = b
a-----
b-----

线程1执行了a判断进入锁,锁了lock1对象进入耗时操作,另外一个线程2进入锁了lock2对象也进入了耗时操作,这时,lock1耗时已完毕执行了lock2操作,然而lock2对象还没有解锁,所以线程1被锁死无法释放而继续等待,这时线程2进入lock1对象锁,而lock1却已经发生死锁无法释放,这种情况之下两锁无法释放就会进入死锁状态。

如果服务器发生死锁了,那么我们也不用着急,因为jdk有提供检测是否有死锁现象的工具->jps。

C:\Users\Administrator.PC-20150302IVQW>jps

1984 Jps

3088 ThreadTest

5516

C:\Users\Administrator.PC-20150302IVQW>jstack -l 3088

Found one Java-level deadlock:

=============================

"Thread-1":

waiting to lock monitor 0x00d47074 (object 0x248f6c90, a java.lang.Object),

which is held by "Thread-0"

"Thread-0":

waiting to lock monitor 0x00d46354 (object 0x248f6c98, a java.lang.Object),

which is held by "Thread-1"

Java stack information for the threads listed above:

===================================================

"Thread-1":

at com.xingqiba.DealThread.run(ThreadTest.java:111)

- waiting to lock <0x248f6c90> (a java.lang.Object)

- locked <0x248f6c98> (a java.lang.Object)

at java.lang.Thread.run(Unknown Source)

"Thread-0":

at com.xingqiba.DealThread.run(ThreadTest.java:97)

- waiting to lock <0x248f6c98> (a java.lang.Object)

- locked <0x248f6c90> (a java.lang.Object)

at java.lang.Thread.run(Unknown Source)

Found 1 deadlock.

使用jps命令我们可以查看死锁的id,然后使用jstack命令根据id查看该死锁的情况:

Found one Java-level deadlock:这句话表示发现1个java级别的死锁

"Thread-1":

waiting to lock monitor 0x00d47074 (object 0x248f6c90, a java.lang.Object),

which is held by "Thread-0"

"Thread-0":

waiting to lock monitor 0x00d46354 (object 0x248f6c98, a java.lang.Object),

which is held by "Thread-1"

第一句话表示Thread-1这个名字的线程正在等待object,内存为0x248f6c90这个位置的地址,它是一个java.lang.Object对象,祈求Thread-0释放。

第一句话表示Thread-0这个名字的线程正在等待object,内存为0x248f6c98这个位置的地址,它是一个java.lang.Object对象,祈求Thread-1释放。

"Thread-1":

at com.xingqiba.DealThread.run(ThreadTest.java:111)

- waiting to lock <0x248f6c90> (a java.lang.Object)

- locked <0x248f6c98> (a java.lang.Object)

at java.lang.Thread.run(Unknown Source)

"Thread-0":

at com.xingqiba.DealThread.run(ThreadTest.java:97)

- waiting to lock <0x248f6c98> (a java.lang.Object)

- locked <0x248f6c90> (a java.lang.Object)

at java.lang.Thread.run(Unknown Source)

这两段话比较重要,它说明了两个死锁的位置,方便我们解决死锁的问题

学完synchronized同步块同步数据之后,我们来学一学另一个多线程同步关键字volatile

1.9.死循环

class PrintString {

	private boolean isContinuePrint = true;

	public boolean isContinuePrint() {
		return isContinuePrint;
	}

	public void setContinuePrint(boolean isContinuePrint) {
		this.isContinuePrint = isContinuePrint;
	}

	public void printStrngMethod() {
		try {
			while (isContinuePrint) {
				System.out.println(isContinuePrint);
				System.out.println("run printStringMethod threadName = "
						+ Thread.currentThread().getName());
				Thread.sleep(1000);
			}
		} catch (Exception e) {

		}
	}

}
public static void main(String[] args) {
	PrintString printString = new PrintString();
	printString.printStrngMethod();
	System.out.println("我要停止它!stopThread="
			+ Thread.currentThread().getName());
	printString.setContinuePrint(false);
}
运行结果:
true
run printStringMethod threadName = main
true
run printStringMethod threadName = main
true
run printStringMethod threadName = main
true
run printStringMethod threadName = main

为什么会死循环呢?很明显,因为模块是在主线程调用,调用printStrngMethod()函数的时候进入了死循环,后面的代码块无法被执行,所以不断执行输出:

true

run printStringMethod threadName = main

那么如何解决问题呢?把代码放入Thread进行异步处理。

私有内存和公有内存是两个模块:

volatile private boolean isContinuePrint = true;

在私有内存中加关键字volatile关键字可以同步私有内存和公有内存。

public void setContinuePrint(boolean isContinuePrint) {
	this.isContinuePrint = isContinuePrint;
}

如果在多线程中执行了死循环没有加volatile关键字就算你在主线程

public void setContinuePrint(boolean isContinuePrint) {
	this.isContinuePrint = isContinuePrint;
}

设置了false,也是没有用的,因为你更新的是公有内存的栈,而私有的内存栈没有被修改,所以会出现死循环不停止。那么如何将公有内存的栈去更新私有的栈,去同步数据呢?在私有内存前加volatile关键字。

2.0.使用关键字volatile更新数据

class MyThread extends Thread{
	volatile public static int count;

	private static void addCount(){
		for (int i = 0; i < 100; i++) {
			count++;
		}
		System.out.println("count=" + count);
	}

	@Override
	public void run() {
		super.run();
		addCount();
	}

}
public static void main(String[] args) {
	MyThread[] myThreads = new MyThread[100];
	for (int i = 0; i < myThreads.length; i++) {
		myThreads[i] = new MyThread();
	}
	for (int i = 0; i < myThreads.length; i++) {
		myThreads[i].start();
	}
}
运行结果:
count=8924
count=9024
count=9124
count=9224
count=9324
count=9424
count=9524
count=9624
count=9724
count=9824
count=9924

值出现了脏读了,可见volatile不能同步函数,那么如何解决这个问题呢?因为要同步函数,那么在函数前加synchronized。

2.1.使用原子类进行修改操作

class AddCountThread extends Thread {

	private AtomicInteger count = new AtomicInteger(0);

	@Override
	public void run() {
		super.run();
		for (int i = 0; i < 100; i++) {
			System.out.println(count.incrementAndGet());
		}
	}

}
public static void main(String[] args) {
	AddCountThread countThread = new AddCountThread();
	Thread t1 = new Thread(countThread);
	t1.start();
	Thread t2 = new Thread(countThread);
	t2.start();
	Thread t3 = new Thread(countThread);
	t3.start();
	Thread t4 = new Thread(countThread);
	t4.start();
	Thread t5 = new Thread(countThread);
	t5.start();
}
运行结果:
492
493
494
495
496
497
498
499
500

这样值一样不会出现脏读现象了。

2.1.原子类也并不完全安全

class MyService {

	public AtomicLong atomicLong = new AtomicLong();

	public void addNum() {
		System.out.println(Thread.currentThread().getName() + "加了100之后的值就是:"
				+ atomicLong.addAndGet(100));
		atomicLong.addAndGet(1);
	}

}

class MyThread extends Thread {

	MyService myService;

	public MyThread(MyService myService) {
		this.myService = myService;
	}

	@Override
	public void run() {
		super.run();
		myService.addNum();
	}

}
public static void main(String[] args) {
	try {
		MyService service = new MyService();
		MyThread[] myThreads = new MyThread[10];
		for (int i = 0; i < myThreads.length; i++) {
			myThreads[i] = new MyThread(service);
		}
		for (int i = 0; i < myThreads.length; i++) {
			myThreads[i].start();
		}
		Thread.sleep(1000);
		System.out.println(service.atomicLong.get());
	} catch (Exception e) {

	}
}
运行结果:
Thread-0加了100之后的值就是:100
Thread-3加了100之后的值就是:300
Thread-1加了100之后的值就是:200
Thread-5加了100之后的值就是:403
Thread-7加了100之后的值就是:504
Thread-9加了100之后的值就是:605
Thread-2加了100之后的值就是:706
Thread-4加了100之后的值就是:807
Thread-6加了100之后的值就是:908
Thread-8加了100之后的值就是:1009
1010

值一样出现脏读现象,那么如何解决问题呢?一样使用synchronized关键字同步数据

public synchronized void addNum() {
	System.out.println(Thread.currentThread().getName() + "加了100之后的值就是:"
			+ atomicLong.addAndGet(100));
	atomicLong.addAndGet(1);
}
运行结果:
Thread-1加了100之后的值就是:100
Thread-3加了100之后的值就是:201
Thread-5加了100之后的值就是:302
Thread-7加了100之后的值就是:403
Thread-9加了100之后的值就是:504
Thread-0加了100之后的值就是:605
Thread-2加了100之后的值就是:706
Thread-4加了100之后的值就是:807
Thread-6加了100之后的值就是:908
Thread-8加了100之后的值就是:1009
1010

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或某一个代码块。它包含两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。

学习多线程并发,要着重"外练互斥,内修可见",这是掌握多线程、学习多线程并发的重要技术点。

时间: 2024-08-19 22:33:27

第二章:对象及变量的并发访问的相关文章

第二章 对象以及变量的并发访问

synchronied 对象监视器为Object时的使用,或者监视器为Class时的使用. 方法中的变量不存在非线程安全问题,永远都是线程安全的,这是方法内部的变量是私有的特性造成的. 1 synchronized的使用 在方法前加关键字synchronized即可. 1)A线程先持有object对象的Lock锁,B线程可以异步的方式调用object对象中的非synchrionized类型的方法. 2)A线程先持有object对象的Lock锁,B线程如果在这时调用了object对象中的synch

java多线程(对象和变量的并发访问)

在现实开发中,我们写的线程肯定会有不同的实例在执行,此时就可能会出现"非线程安全问题",非线程安全就是:多个线程对同一个对象中的实例变量进行并发访问时候,有可能A和B线程同时读取到数据,先后进行更改,此时,该变量就不是我们期望的数据,也就是通常所说的"脏数据" 实例变量非线程安全 需要注意的是,方法中的变量是不存在非线程安全问题的,这是因为方法内部的变量都是私有的. 如果多个线程共同访问了一个对象中的实例变量,则可能会出现线程安全问题.看下面代码: public c

Java多线程编程核心 - 对象及变量的并发访问

1.什么是“线程安全”与“非线程安全”? “非线程安全”会在多个线程对同一对象总的实例变量进行并发访问时发生,产生的后果是“脏读”,也就是取到的数据其实是被更改过的. “线程安全”是以获得的实例变量的值是经过同步处理的,不会出现脏读的现象. 2.非线程安全例子?怎么解决? 非线程安全 package com.jvm.thread; public class HasSelfPrivateNum { private int num =  0; public void add(String usern

(二)对象以及变量的并发访问--synchronized的使用细节,用法

具体的记录synchronized关键的各种使用方式,注意事项.感觉一步一步跟我来都可以看懂滴 大致是按照以下思路进行书写的.黑体字可以理解为结论, 1.synchronized锁的是什么? 2.synchronized能够锁住所有方法吗? 3.synchronized能够用来锁住一个方法之中的部分代码吗? 4.synchronized能够锁住除了this以外的其他对象吗?有什么用?有什么需要注意的? -----------------------------------------------

对象及变量的并发访问一

一.多个线程操作一个对象实例 当两个线程同时访问一个没有同步的方法,如果两个线程同时操作业务对象中的实例变量,则有可能会出现“非线程安全问题”. 1 package concurrent; 2 /** 3 * 测试不同线程操作同一个实例变量线程安全问题 4 * @author foolishbird_lmy 5 * 6 */ 7 class ThePrivateNumber{ 8 private int num = 0; 9 10 public synchronized void addI(St

多线程--对象及变量的并发访问

1 . 多个线程访问多个对象JVM会创建多个锁.2 . 静态方法是以类为单位进行同步的--对于同一个类中的所有静态方法,在同一时间内,只允许有一个线程执行其中的一个静态方法,其余想要进入这些方法的线程都必须挂起等待.非静态方法是以对象为单位进行同步的.3 .假设现有两个线程A和B,一个object对象,当线程A调用object对象的一个同步方法M1时,线程A就获得了M1方法所在对象的锁,所以其他线程必须等待线程A执行完毕之后才能调用方法M1,如果线程B调用object的同步方法M2,必须等待线程

Java多线程编程(二)对象及变量的并发访问

一.synchronized同步方法 1.方法内的变量为线程安全 2.实例变量非线程安全 3.多个对象多个锁 4.synchronized方法与锁对象 5.脏读 6.synchronized锁冲入 7.出现异常,锁自动释放 8.同步不具有继承性 二.synchronized同步语句块 1.synchronized方法的弊端 2.synchronized同步代码块的使用 3.用同步代码块解决同步的弊端 4.一半异步,一半同步 5.synchronized代码块间的同步性 6.验证同步synchro

第二章 数据,变量和计算

头文件 #include <>或#include "" <> 强制类型转换 double a=1.6; cout<<static_cast<int>(a)<<endl; 输出为1 注意旧的强制类型转换为int(a),这比新的更容易出错,得不到想要的结果,所以推荐static_cast<转换成的类型>() 自动判断数据类型 auto n=16; 自动判断n的类型为int const auto e=1.1; 自动判断静

Java第二章----对象和类

从第一章到第二章整整隔了一个月的时间,这速度也是慢的无语了.因为这个月负责开发公司一个SaaS类型APP,忙的昏天暗地终于上线了,这才有时间写个博客.本章还是以概念为主,有点枯燥重在理解. 第一节:对象 名词解释 OOA-Object Oriented Analysis-面向对象分析 OOD-Object Oriented Design-面向对象设计 OOP-Object Oriented Programming-面向对象程序设计 面向对象基本特性 万物皆可为对象:任何一件事物都可以表示为程序中