Java基础学习

在java中
final用来定义常量
final static用来定义类常量,也就是类中的方法都可以使用这个常量。
public final static表明别的类中的方法,也可以访问这个常量。
static声明的变量可以理解成是类变量,只存在一个这个变量。是可变的,只是范围上来说是类级别的,要是想定义常亮,那么结合final使用
&&与||是短路方式求职的,如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。

静态方法建议试用类名来直接访问,不要使用对象名来操作。静态变量也是一样的。

对于String的比较,一定要使用equals来比较2个字符串的值是否相等,不能使用==,==是对比的2个字符串是否防止在了同一个位置上。
要是放在了同一个位置上,那么是相等的,要是不是同一个位置,那么就不相等了。


jdk5.0开始引入了StringBuilder,这个类的前身是StringBuffer,StringBuffer允许采用多线程的方式执行添加或删除字符的操作。
StringBuilder的效率更高。

不规则数组定义
int[][] odds = new int[4][];
for (int n=0;n<4;n++){
odds[n]=new int[n+1];
}

java中也是有类构造器的。这个概念有些忘记了。
类构造器 
构造器与类同名
每个类可以有一个以上的构造器
构造器可以有0个,1个或1个以上的参数
构造器没有返回值
构造器总是伴随new操作一起调用

this有两个用途:一是引用隐式参数,二是调用该类其他的构造器。super也有2个用途:一是调用超类的方法,二是调用超类的构造器。

要返回一个可变数据域的拷贝,应该使用克隆。对象克隆是指存放在另一个位置上的对象副本。

有时,可能希望将一个计算代码划分成若干个独立的辅助方法,这些辅助方法不应该成为公有接口的一部分。这是由于他们往往与当前的实现机制非常紧密,
或者需要一个特别的协议以及一个特别的调用次序,这样的方法最好被设计为private的。

final的变量,在构建对象时必须初始化这样的域,并且在后面的操作中,不能在对他进行修改。

可变类和不可变类(Mutable and Immutable Objects)的初步定义:
可变类:当你获得这个类的一个实例引用时,你可以改变这个实例的内容。
不可变类:当你获得这个类的一个实例引用时,你不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。
如何创建一个自己的不可变类:
.所有成员都是private
.不提供对成员的改变方法,例如:setXXXX
.确保所有的方法不会被重载。手段有两种:使用final Class(强不可变类),或者将所有类方法加上final(弱不可变类)。
.如果某一个类成员不是原始变量(primitive)或者不可变类,必须通过在成员初始化(in)或者get方法(out)时通过深度clone方法,来确保类的不可变。


--------------------------简单的理解上,不可变就是没有set方法去改变他的属性值-------------------------

final关键字到底修饰了什么?

final使得被修饰的变量"不变",但是由于对象型变量的本质是“引用”,使得“不变”也有了两种含义:引用本身的不变,和引用指向的对象不变。

引用本身的不变:
final StringBuffer a=new StringBuffer("immutable");
final StringBuffer b=new StringBuffer("not immutable");
a=b;//编译期错误

引用指向的对象不变:
final StringBuffer a=new StringBuffer("immutable");
a.append(" broken!"); //编译通过

可见,final只对引用的“值”(也即它所指向的那个对象的内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,
final是不负责的。这很类似==操作符:==操作符只负责引用的“值”相等,至于这个地址所指向的对象内容是否相等,==操作符是不管的。


理解final问题有很重要的含义。许多程序漏洞都基于此----final只能保证引用永远指向固定对象,不能保证那个对象的状态不变。在多线程的操作中,一个对象会被多个线程共享或修改,
一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。一个错误的解决方法就是在此对象新建的时候把它声明为final,意图使得它“永远不变”。其实那是徒劳的。

对于两种不同的类属性,static属性与instance属性,初始化的时机是不同的。instance属性在创建实例的时候初始化,static属性在类加载,也就是第一次用到这个类的时候初始化,
对于后来的实例的创建,不再次进行初始化。这个问题会在以后的系列中进行详细讨论。

方法中的参数采用的是值拷贝的方式,传递过来的参数的原来的指向不变。

多个方法有相同的名字,不同的参数,便产生了重载。

调用另一个构造器
如果构造器的第一个语句形如this(..),这个构造器将调用同一个类的另一个构造器。

初始化块
无论使用哪个构造器构造对象,id域都在对象初始化块中被初始化,首先运行初始化块,然后才运行构造器的主体部分。

调用构造器的具体处理步骤:
1所有数据域被初始化为默认值
2按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块
3如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
4执行这个构造器的主体


静态初始化块
static{
Random generator = new Random();
nexId = generator.nextInt(1000);
}

java静态初始化块,会在类第一次加载的时候,进行初始化。初始化块,在每次新建对象的时候都要执行。

finalize方法将在垃圾回收器清除对象之前调用。不建议使用这个方法来释放资源。

假如类路径是/home/user/classdir:.:/home/user/archives/archive.jar
虚拟机要搜寻com.horstmann.corejava.Employee类文件,它首先查看jre/lib和jre/lib/ext目录下的归档文件中所存在的
系统类文件。要是在那里找不到相应的类文件,再查看类路径。于是查看:
/home/user/classdir/com/horstmann/corejava/employee.class
com/horstmann/corejava/employee.class在当前目录开始
com/horstmann/corejava/employee.calss 在/home/user/archives/archive.jar中


试用-calsspath制定类路径
java -calsspath /home/user/classdir:.:/home/user/archives/archive.jar MyProg.java
可以设置CLASSPATH环境变量来完成这个操作

java中的继承 class manager extends employee{}
所有的继承都是公有继承。

一个对象变量可以引用多种实际类型的现象被称为多态,在运行时能够自动地选择调用哪个方法的现象称为动态绑定。

不允许扩展的类被称为final类。类中的方法也可以被声明为final,这样子类就不能覆盖这个方法(final类中的所有方法自动地成为final方法)


在类型转换的时候,如果是父类型转换成子类型,那么试图在继承链上进行向下的类型转换的时候,要是类型不对,那么在运行的时候会
报classcastexception,因此,要养成良好的习惯,在类型转换之前,先检查一下是否能够成功的转换,再将超类转换成子类之前,
使用instanceof,一般情况下,应该尽量少用类型转换盒instanceof运算符。


抽象方法没有具体的方法实现,有抽象方法的类必须要定义成抽象类。抽象类中可以有别的具体的方法实现。抽象类不能被实例化,就是不能创建
这个类的对象。可以定义一个抽象类的对象变量,但是它只能引用非抽象子类的对象。


人们希望超类中的某些方法允许被子类访问,或允许子类的方法访问超类的某个域。为此,需要将这些方法或域声明为protected.


在java中,只有基本类型不是对象,所有的数组类型,不管是对象数组还是基本类型的数组都扩展于object类。


getClass与instanceof的区别,试用instanceof的时候无法区别子类,子类的instanceof 是父类的,要是想获取子类的类,要试用getClass.
m is a instanceof Employee
m's getclass ==>class ForTest.Manager


编写equal的典型用法
public boolean equals(Object otherObject){
if(this==otherObject){
System.out.println("this ===otherOBjects");
return true;
}else{
System.out.println("this !=====otherOBjects");
}
if(otherObject == null)return false;
if(getClass() !=otherObject.getClass()){     ------这个事针对在子类中的,要是由超类类型定义是否相同可以使用instanceof
System.out.println("getClass !===otherOBject.getClass");
return false;
}else{
System.out.println("getClass===otherObject.getClass");
}
Employee other = (Employee)otherObject;
return name.equals(other.name)&&salary==other.salary ;
}


方法参数可变的方法如下:
public static double max(double ... values)


protect  子类可以直接访问protected实例域,同一个包的所有类可以访问protected域。


可以用instanceof来检查一个对象是否实现了某个特定的接口
if(anobject instanceof comparable){..}
接口中的方法默认自动的设置成public,接口中的域将被自动的设置为public static final


每个类只能拥有一个超类,但却可以实现多个接口。


接口与抽象类
为什么不直接使用抽象类,在抽象类中定义一些抽象方法,让下面的子类去实现,这样就不用接口了?
原因:要是使用抽象类,那么每个类只能扩展于一个类,假设employee类已经扩展于一个类,如person,就不能再扩展第二个类了。
class employee extends person,comparable
但是可以向下面这样来实现多个接口
class employee extends person implements comparable,cloneable


默认的克隆是浅克隆,也就是说当类中的域是基本类型的时候没有问题,但是对象中包含了子对象的引用,拷贝的结果会使得2个域引用
同一个子对象。
相应的深度克隆也就是对象的完全克隆,不管域是什么样的类型。在类考虑克隆的时候,要做下面的判断:
1默认的clone方法是否满足要求
2默认的clone方法是否能通过调用可变子对象的clone得到修补
3是否不应该使用clone
要实现克隆,1实现Cloneable接口2使用public访问修饰符重新定义clone方法,深度克隆的一个例子:
public Employee clone() throws CloneNotSupportedException{
Employee cloned = (Employee)super.clone();
cloned.hireDay =(Date)hireDay.clone();
return cloned;
}
所有的数组类型包含一个clone方法,可以用来拷贝一个新数组。


内部类的使用原因:
1内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据
2内部类可以对同一个包中的其他类隐藏起来
3当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。


类的访问性,只有内部类可以是私有的,常规类只可以具有包可见性,或公有可见性。
局部内部类就是内部类放到了方法的内部,只有这个方法能看到这个类,所以就没有访问说明符。
匿名内部类,之创建一个内部类的对象,没有名字,通常的定义如下:
new superType(construction parameters){
inner class methods and data
}


静态内部类,有时候,使用内部类只是为了将一个类隐藏在另外一个类的内部,并不需要内部类引用外围对象。
代理:使用代理可以再运行时创建一个实现了一组给定接口的新类。该功能只有在编译时无法确定需要实现哪个接口时才有必要使用。


泛型类可以看成是普通类的工厂,泛型方法可以放在常规类中。类型变量的限制,当需要类型变量满足一定的条件的时候,比如该类型变量所属
的类要实现comparable接口,实现方法如下:
public static <T extends Comparable & Serializable> T min(T[] a)...
类型通配符
<?>这个代表任意的对象或<? extends List>或<? super StringBuilder>
使用通配符声明的名称所参考的对象,没有办法再对它加入新的信息。只能取得它当中的西悉尼或是移除当中的信息。
extends泛型类与实现泛型接口与普通类的差不多


----------------排查问题方法
查看代码的堆栈的信息,可以在相应代码下面使用Thread.dumpStack();方法来打印出堆栈。
在虚拟机启动的时候,设置-verbose,可以打印类加载过程,可以诊断由于类路径引发的问题。
使用jconsole,在java se 6之前,需要使用-Dcom.sun.management.jxmremote 参数来启动程序
java -Dcom.sun.management.jmxremote MyProgram


jmap -dump:format=b,file=outfile 3024可以将3024进程的内存heap输出出来到outfile文件里,再配合MAT(内存分析工具(Memory Analysis Tool),使用参见:
http://blog.csdn.net/fenglibing/archive/2011/04/02/6298326.aspx)或与jhat (Java Heap Analysis Tool)一起使用,能够以图像的形式直观的展示当前内存是否有问题。
64位机上使用需要使用如下方式:
jmap -J-d64 -heap pid


多进程与多线程有哪些区别?本质的区别在于每个进程拥有自己的一整套变量,而线程则共享数据。共享数据有风险,共享变量使得线程之间的
通讯更有效,更容易。在操作系统级,与进程相比,线程更“轻量级”,创建,撤销一个线程比启动新进程的开销要小得多。


在一个单独的线程中执行一个任务的简单过程:
1将任务代码移动到实现了Runnable接口的类run方法中。
2创建一个类对象:
Runnable r = new MyRunnable();
3由Runnable创建一个Thread对象
Thread t = new Thread(r);
4启动线程
t.start();


线程的6种状态
new:该线程还没有开始运行。程序还没有开始运行线程中的代码。
runnable:调用start方法,线程处于runnable状态。一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的
时间。一个线程开始运行,不必始终保持运行,运行中的线程被中断,目的是为了给别的线程机会,抢占式调度系统给每一个可运行线程一个时间片
来执行任务,当时间片用完,操作系统剥夺该线程的运行权,并给另一个线程运行机会。选择下一个线程时,操作系统考虑线程的优先级。
blocked:线程试图获取一个内部的对象锁,而该锁被其他线程持有,则线程进入阻塞状态
waiting:当线程等待通知的时候,进入等待状态。
timed waiting:等待超时或通知时就进入该状态
terminated,正常退出,另一个是没有捕获的异常终止了run方法意外死亡。


守护线程:
通过调用t.SetDaemon(true)将线程转换为守护线程。守护线程的唯一用途是为其他线程提供服务。
未捕获异常处理器
线程的run方法不能抛出任何被检查到的异常,但是不被检测的异常会导致线程终止。这样,线程就死亡了。
不需要任何catch子句来处理可以被传播的异常,相反,就在线程死亡之前,异常被传递到一个用于未捕获异常的处理器。


线程组是一个可以统一管理的线程集合。默认情况下,创建的所有线程属于相同的线程组。
条件对象:一个锁对象可以有一个或多个相关的条件对象,我的理解就是,满足一定条件(也就是这个条件对象的时候),释放锁。
等待获得锁的线程和调用await方法的线程存在本质上的不同,一旦一个线程调用await方法,它进入该条件的等待集。
当锁可用时,该线程不能马上解除阻塞,相反,它处于阻塞状态,直到另一个线程调用同一条件上的signalAll方法为止。


锁和条件的关键之处:
锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码。
锁可以管理试图进入被保护代码段的线程。
锁可以拥有一个或多个相关的条件对象。
每个条件对象管理那些已经进入被保护的代码段但还不能运行的线程。


监视器:在不需要程序员考虑如何加锁的情况下,就可以保证多线程的安全性,最成功的解决方案之一是监视器。


监视器是只包含私有域的类。
每个监视器类的对象有一个相关的锁。
使用该锁对所有的方法进行加锁。
该锁可以有任意多个相关条件。

volatile关键字为实例域的同步访问提供了一种免锁机制,如果一个域为volatile,那么编译器和虚拟机就知道该域是可能被另一个
线程并发更新的。


读写锁
使用读写锁的必要步骤:
1构造一个ReentrantReadWriteLock对象
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
2抽取读锁和写锁
private Lock readLock = rwl.readLock();
private Lock writeLock = rwl.writeLock();
3对所有的访问者加读锁
public double getTotalBalance(){
readLock.lock();
try{..}
finally{readLock.unlock();}
}
4对所有的修改者加写锁
public void transfer(...)
{
writeLock.lock();
try{..}
finally{writeLock.unlock();
}


在协调多个线程之间的合作时,阻塞队列是一个有用的工具。
callable和future,callable是有返回值的。该借口类似Runnable。返回的结果可以传递给Future。
FutureTask包装器实现了Future和Runnable的接口,是一种非常便利的机制。


执行器类有许多静态工厂方法用来构建线程池。
信号量Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
比如在Windows下可以设置共享文件的最大客户端访问个数。 


barrier:当所有部分都准备好,需要把结果组合在一起。当一个线程完成了它的那部分任务后,我们让他运行到barrier。一旦所有的线程
都达到了这个barrier,barrier就撤销,线程就可以继续运行。


Exchanger可以在两个线程之间交换数据,只能是2个线程,他不支持更多的线程之间互换数据。当线程A调用Exchange对象的exchange()方法后,他会陷入阻塞状态,直到线程B也调用了exchange()方法,然后以线程安全的方式交换数据,
之后线程A和B继续运行
-------------------------------------------------------------------------------------


异常的处理:
try{
//1
code than might throw exceptions
//2
}
cath(IOException e)
{
//3
show error dialog
//4
}
finally{
//5
g.dispose();
}
//6


有下列三中情况会执行finally子句:
1代码没有抛出异常,执行顺序1、2、5、6
2抛出一个在catch子句中捕获的异常1、3、4、5、6,要是在catch中抛出一个异常,执行1/3/5
3代码抛出一个异常,但这个异常不是由catch子句捕获的。这种情况下执行1、5
断言机制允许在测试期间向代码中插入一些检查语句,在代码发布时,这些插入的检测语句会被自动移走。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值