黑马程序员————Java的基础知识详解

          ————Android培训Java培训、期待与您交流!————


                              一.克隆技术

1.1 简介

    克隆分为两种,一种是浅层克隆,一种是深层克隆。深层克隆具有克隆的递归性,也就是说,只要克隆对象有引用对象,那么引用对象的所在类也必须克隆,知道最底层的类只包含基本类型元素。例如:类A,类B,类C  A包含B的引用,B包含C的引用。如果想对A深层克隆;A,B,C,都需继承Cloneable接口,而且需要重写 Object 类的clone()方法,即可实现深度克隆。如果类C还包含其他类的引用,则还要重复前面的步骤,知道其中一个类的所有元素为基本类型即可。

1.2 浅层克隆

public class Test {
	public static void main(String[] args) throws Exception {
	B b=new  B();
	B b1=(B) b.clone();
	System.out.println(b1.str);
	b1.exam.x=10;
	System.out.println(b.exam.x);//浅层克隆,Exam类的对象没有克隆出,还是和原来原来的一样。
	}}
class Exam
{
public int x=20;
public void print()
{
System.out.println(x);
}
}
class B implements Cloneable
{
public Exam exam=new Exam();
public String str="china";
@Override
public Object clone()
{
Object obj=null;
try {
	return super.clone();
} catch (CloneNotSupportedException e) {
	e.printStackTrace();
}
return obj;
}
}

1.3 深度克隆

public class Test {
	public static void main(String[] args) throws Exception {
	B b=new  B();
	B b1=(B) b.clone();
	System.out.println(b1.str);
	b1.exam.x=10;
	System.out.println(b.exam.x);//浅层克隆,Exam类的对象没有克隆出,还是和原来原来的一样。
	}}
class Exam implements Cloneable
{
public int x=20;
public void print()
{
System.out.println(x);
}
public Object clone()
{
Object obj=null;
try {
	return super.clone();
} catch (CloneNotSupportedException e) {
	e.printStackTrace();
}
return obj;
}
}
class B implements Cloneable
{
public Exam exam=new Exam();
public String str="china";
@Override
public Object clone()
{
Object obj=null;
try {
	 obj=super.clone();
	 exam=(Exam) this.exam.clone();//如果你要用this,那么该语句一定要在上面语句的下面。this指向当前对象引用,也就是指向obj,否则,位置倒过来,那么this就会指向
	 //你在使用该类时创建的对象。
} catch (CloneNotSupportedException e) {
	e.printStackTrace();
}
return obj;
}
}

1.4 克隆和用new 创建对象的区别

    克隆它是以当前对象的值为基础,new 创建对象是以类所指的属性为基础。通俗的说,加入类A有个属性X=2;那么在克隆时,加入类A的对象a,在克隆之前改变了X的值(X=1),那么克隆出来对象的属性就是改变之后的值(X的值就是1)。而new 创建的对象就是和类指定的属性相同(X的值为2)。

                                          

                                                  二.序列化技术

2.1 序列化基础

    条件:如果某个类的对象需要序列化,那么它就要继承Serializable接口(该接口为标识接口,无任何元素)要么就继承Externalizable接口。

    注意:在序列化的时候,如果父类不需要序列化,则另创建个有参构造器,在有参构造器中初始化。否则序列化的时候,父类会被序列化。同时父类如果有其它的构造器时,必须要为父类提供公有无参构造函数,因为在创建对象和反序列化时,子类会首先调用父类的构造函数,然后才调用自己的构造函数,一般在反序列化时,用无参构造函数对父类进行对象初始化。

    要点:如果你不想让某个值不被序列化,你有两种方式:第一种你就是让该值加上关键字(transient),第二种

就是让不想被序列化的值放在没有被序列化的父类的公有有参构造器中,不要显示赋值,否则由于继承机制,它也会被序列化。不过第一种的好处是你可以稍后通过ObjectOutputStream的相关方法让它重新被写进序列化流中。

    知识点:接口(SerializableExternalizable) 类(ObjectInputStreamObjectOutputStream) 同时在序列化时的序列化ID(SerialVersionUID)为一个固定值,在反序列化时该值要和序列化时的值保持一致,否则会出现错误。序列化只针对对象非类,但可以通过额外的手段来实现对类属性的存取。

    序列化存储规则:在对对象进行序列化时,如果对同一个对象(注意:同一个对象和同一个对象的不同引用不是一个概念)序列化存放,那么只会存进第一个对象引用。

2.2 编程案例

public class Test1 {//最原始的默认序列化和反序列化,只需继承Serializable接口即可,但性能不高。

	public static void main(String[] args) {
		try {
			ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
			Exam1 exam1=new Exam1();
			out.writeObject(exam1);
			out.close();
            ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
			Exam1 ex1 = (Exam1) oin.readObject();
		 
			System.out.println(ex1.name);
			 
			oin.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
}

	}
class Exam1 implements Serializable
{
	 private static final long serialVersionUID = 1L;

	 public String name="123";
	
		}
public class Test1 {//对同一个对象序列化存放只有一次,哪怕你创造多个类的引用也无助于事,来对引用存放也无济于事.

	public static void main(String[] args) {
		try {
			ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
			Exam1 exam1=new Exam1();//引用1
			out.writeObject(exam1);//引用1存放
			Exam1 exam2=exam1;//引用2
			exam2.name="456";//修改原来的对象属性
			out.writeObject(exam2);//存放引用2
			out.close();
            ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
			Exam1 ex1 = (Exam1) oin.readObject();//获取引用1
			Exam1 ex2 = (Exam1) oin.readObject();//获取引用2
			System.out.println(ex1.name);//输出123
			System.out.println(ex2.name);//输出123
			oin.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
}

	}
class Exam1 implements Serializable
{
	 private static final long serialVersionUID = 1L;

	 public String name="123";
	
		}
public class Test {
	public static void main(String[] args) throws Exception {
	 
			ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
			Exam exam=new Exam();
			out.writeObject(exam);
			out.close();
            ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
			Exam copy = (Exam) oin.readObject();
			System.out.println(copy.age);
		    System.out.println(copy.number);
		    System.out.println(copy.name);
		    System.out.println(copy.X);
			oin.close();
}
}

class Exam implements Serializable
{
	 private static final long serialVersionUID = 1L;

		public transient  String name = "武汉大学";//不会被序列化
	    public static int age=110;//不会被序列化
	    public long number=19000523453712L;
	    List<Object> list=new ArrayList<Object>();//如果该容器包含的对象过多,会造成程序性能降低.
	    public transient int X=1949;
		private void writeObject(ObjectOutputStream out) {
			try {
				out.defaultWriteObject();
				out.writeUTF(name);//可以调用该方法让有transient关键字修饰的name实现序列化
				out.writeInt(X);//
				out.writeInt(age);//虽然age不会被默认序列化,但可以调用该方法让其序列化,类的属性如果想被序列化,一定要放在最后面。
			  
				out.flush();
			} catch (IOException e) {}		
		}
		private void readObject(ObjectInputStream in) {
	         try {
				in.defaultReadObject();
				String name=in.readUTF();
				this.name=name;
				int x=in.readInt();
				this.X=x;
			} catch (ClassNotFoundException | IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	
}
public class Test1 {//利用序列化对对象的替代,一般用于如果单例需要序列化时的解决方法

	public static void main(String[] args) throws  Exception {
		 
			ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
			Exam1 exam1=Exam1.exam1;
			out.writeObject(exam1);
			out.close();
            ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
			Exam1 ex1 = (Exam1) oin.readObject();
			System.out.println(exam1==ex1);
		 
			oin.close();
}
	}
class Exam1 implements Serializable
{
	 private static final long serialVersionUID = 1L;
	 public final static Exam1 exam1=new Exam1();
	 private Exam1()
	 { 
	 }
	 public transient String name="123";
	 private void writeObject(ObjectOutputStream out) throws IOException
	 {
		out.defaultWriteObject();
		out.writeUTF(name);
	 }
	 private void  readObject(ObjectInputStream in) throws ClassNotFoundException, IOException
	 {
	 in.defaultReadObject();
	 String name=in.readUTF();
	 this.name=name;
	 }
	 private Object readResolve()
	 {
	 return  exam1;
	 }
	 }
public class Test1 {//利用序列化对对象的替代,将需要被序列化的对象替换掉

	public static void main(String[] args) throws  Exception {
		 
			ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
			Exam1 exam=new Exam1("中华人民共和国");
			out.writeObject(exam);
			out.close();
            ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
			Exam1 ex1 = (Exam1) oin.readObject();
			System.out.println(ex1.name);//期待输出中华人民共和国,实际输出1949
		 
			oin.close();
}
	}
class Exam1 implements Serializable
{
	 private static final long serialVersionUID = 1L;
	 public transient String name="1949";
	 private final static Exam1 exam1=new Exam1();
	 private Exam1()
	 { 
	 }
	 public Exam1(String name)
	 {
	 this.name=name; 
	 }
	 private void writeObject(ObjectOutputStream out) throws IOException
	 {
		out.defaultWriteObject();
		out.writeUTF(name);
	 }
	 private void  readObject(ObjectInputStream in) throws ClassNotFoundException, IOException
	 {
	 in.defaultReadObject();
	 String name=in.readUTF();
	 this.name=name;
	 }
	 private Object writeReplace()
	 {
	 return  exam1;
	 }
	 }
public class Test1
{   public static void main(String[] args) throws  Exception//如果父类的属性显示初始化,则会通过继承原则实现序列化
	{
	ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
     Son son=new Son();
	 out.writeObject(son);
	 out.close();
     ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
	 Son copy = (Son) oin.readObject();
	 System.out.println(copy.x);
	oin.close();
}
}
class Parent
{
int x=10;
int y=20;
public Parent()
{
}
}
class Son extends Parent implements Serializable
{
 
	private static final long serialVersionUID = 1L;
int z=30;	
}
public class Test1
{   public static void main(String[] args) throws  Exception//如果不想被序列化,则重新创建一个有参构造器初始化
	{
	ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
     Son son=new Son();
	 out.writeObject(son);
	 out.close();
     ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
	 Son copy = (Son) oin.readObject();
	 System.out.println(copy.x);
	 oin.close();
}
}
class Parent
{
int x;//不要显示赋值
public Parent()//不要在默认构造器中初始化
{
}
public Parent(int x)
{
this.x=x;
}
}
class Son extends Parent implements Serializable
{
private static final long serialVersionUID = 1L;
}

                            三.动态代理技术

3.1 动态代理基础

    条件:InvocationHandler接口的实现类;Proxy类;接口;被代理的类。

3.2 程序解释

public class Test1
{   public static void main(String[] args) throws  Exception  
	{
	TimePerformance f=new TimePerformance();
	InvocationHandler handler=new InvocationHandler(){ 
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
		  Date d=new Date();
		  long start=d.getTime();
		  Object obj=method.invoke(f, args);
		  Date t=new Date();
		  long end=t.getTime();
		  System.out.println("冒泡排序对数组排序所运行时间:"+(end-start));
		  return obj;
	}};
	 Time time=(Time) Proxy.newProxyInstance(f.getClass().getClassLoader(), f.getClass().getInterfaces(), handler);
	 
	 int[]a=TimePerformance.getarray(150, 11, 1790);
	 time.performance(a);
	 for(int d:a)
	 {
	System.out.print(d+" ");
	 }
	
	 
}
}
 
interface Time
{
 public void performance(int a[]);
}
class TimePerformance implements Time
{
 public static int[] getarray(int length,int Minlimit,int Maxlimit)
 {
 int[] array=new int[length];
 for(int i=0;i<length;i++)
 {
   array[i]=(int)(Math.random()*(Maxlimit-Minlimit)+Minlimit);
 }
 return array;
 }
 public void performance(int a[])
 {
 for(int i=1;i<a.length;i++)
 {  boolean flag=true;
	for(int j=a.length-1;j>=i;j--)
	{
	if(a[j]<a[j-1])
	{
	int temp;
	temp=a[j];
	a[j]=a[j-1];
	a[j-1]=temp;
	flag=false;
	}
	}
	if(flag) break;
 }
 }
 
}

                               四.枚举技术

4.1 枚举基本概念

   如果打算自定义自己的方法和属性,那么必须在enum实例序列的最后添加一个分号。而且Java要求必须先定义enum实例。枚举不能通过反射来创建实例对象,枚举的实例对象在枚举中定义,实例是静态常量。

4.2 枚举的用法

     用法一:定义常量;用法二:switch语句;用法三:向枚举中添加新方法;用法四:覆盖枚举的方法;用法五:实现接口;用法六:使用接口组织枚举;用法七:关于枚举集合的使用:java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的key是enum类型,而value则可以是任意类型。 

4.3 编程举例

public class Test {
	public static void main(String[] args) throws Exception {
	 Country c=Country.China;
	 switch(c)
	 {
	 case USA:{System.out.println("世界第一");c=Country.Russia;break;}
	 case China :{System.out.println("世界第二");c=Country.USA;break;}
	 case Russia :{System.out.println("世界第三");c=Country.Russia;break;}
	 case France :{System.out.println("世界第四");c=Country.France;break;}
	 case Britain:{System.out.println("世界第五");c=Country.Britain;break;}	 
	 }	 
	Country[] c1=Country.values();
	for(Country t:c1)
	{ 
	System.out.print(t+"     ");
}
}

enum   Country
{
China("中国"),USA("美国"),Russia("俄罗斯"), France("法国"),Britain("英国");
 
private String name;
private Country(String name)
{
this.name=name;
}
@Override
public String toString() {
return this.name;
}
}
}

                    

                               五.注解技术

5.1 注解简介

     只有以@inferface标识的为注解,不能使用泛型,不能继承其他接口。注解中可以包含多个元素,每个元素可以看成是注解中包含的配置项,通过方法的声明来定义,方法声明不能声明异常,也不能有参数,方法的名称是元素的名称,方法的返回值类型就是元素的类型:基本类型,Class类型,数组类型(只支持单维数组,数组元素为非数组类型),String类型,枚举类型,注解类型。注解中如果只有一个元素,那么可以用value指定,可以通过default关键字来制定该元素的值。注解分为元注解和一般注解。元注解:@Target,@Retention,@Inherited。

5.2 编程举例

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface A {
String name();
int age();
long num();
String Hometown();
}
public class Student {
	@A(name="张三", Hometown ="湖北武汉", age =23, num =19491212054532L)
    public static String name;
	@A(name="张三", Hometown ="湖北武汉", age =23, num =19491212054532L)
    public static int age;
	@A(name="张三", Hometown ="湖北武汉", age =23, num =19491212054532L)
    public static long num;
	@A(name="张三", Hometown ="湖北武汉", age =23, num =19491212054532L)
    public static String  Hometown;
	public static void main(String[] args) {
	 Field[] fields=Student.class.getFields();	
	A a1=fields[0].getAnnotation(A.class);
	A a2=fields[1].getAnnotation(A.class);
	A a3=fields[2].getAnnotation(A.class);
	A a4=fields[3].getAnnotation(A.class); 
	name=a1.name();
	age=a2.age();
	num=a3.num();
	Hometown=a4.Hometown();
	System.out.println(name);
	System.out.println(age);
	System.out.println(num);
	System.out.println(Hometown);

	}

}


                            六.面向对象技术

6.1 类

   对拥有相同特征和行为的对象的总的抽象描述,也即是对拥有相同属性和行为对象的描述。它是个抽象,静态的概念,现实生活中不存在。

6.2 对象

    类的实体对象,拥有状态和行为。它是具体而动态的,现实生活中存在的。

6.3 封装

    封装就是将原来松散的结构通过重新组装在一个被人们叫做类的东西中。将原来的松散结构抽取出两个概念:属性和行为。属性即是对类的特征的描述。行为就是向对象传递消息的途径,它能够将一些信息传递给对象。封装和对数据和行为的隐藏是不同的概念,对数据和行为的隐藏就是该数据和行为对外界透明,外界无法访问到,在类自己的空间中,而封装就是对属性和行为组合到某个类中,仅此而已。就像包一样,它也是封装,把一些类封装在包中,而隐藏是通过封装衍生出来的,一般分为:私有,只有类的内部才可以;默认,类包所属,同一个包中可以访问到。保护,该类的子类可以访问到,或者和该类在同一使用空间。公有:访问无界限。

6.4 继承

     继承虽说就是继承父类的乃至祖宗类的属性,行为。那么请问是不是所有的东西都可以被子类继承?那么答案是否定的,为什么呢,我想任何东西都有自己的特殊特征,那么类的特殊特征就是用private修饰的属性和行为。他们就是不能够被继承的,最近利用反射查看了一下:在父类中声明定义一个属性和方法,结果测试出只有public修饰的属性和行为才能被打印出,其它的都提示出无此属性和行为。虽然测试结果和只有private属性不能够被继承有点距离。但令人纠结的是被protected修饰的属性和方法可以通过子类访问到,就补充了前面反射测试的结果。还有点吊诡的是继承的底层是怎样实现的,目前我有两种猜想:猜想1:子类的实例化从子类的<init>方法开始,从子类的<init>开始,并不代表实例化开始。如是子类实例化要从父类开始实例化机制。那么就可以清楚的了解父类的构造器包含在子类的构造器中,自然而然父类的属性包含在子类的构造器中。猜想2:就是子类只保存父类的引用,子类通过父类引用来完成完成访问父类属性和行为的操作,这样就可以节省很多资源,就不必要对父类的属性和行为保存两份,大大节约内存资源。

6.5 多态

多态有两种方式:对象的多态,方法的多态。对象的多态:父类的引用可以接收子类的对象。方法的多态是多态的核心:方法的重载:方法的重载(方法的名称相同,方法的参数顺序不一样,对返回值没有要求),方法的重载实在同一个类中实现的,方法的重载只和它所调用的对象引用有关系,和对象本身关系不大,因为重载的的分配方式它是静态的,静态分配的方法通常有类方法。重载方法的匹配规则:如果参数是基本类型,那么它会首先匹配自己的类型,如果匹配失败,那么它会宽化处理,通过宽化来处理如果通过宽化处理找到匹配类型就执行,否则它会自动装箱,来匹配自己的类型,如果失败,它就会去寻找自己的父类,父类具有相同的优先级,如果存在参数为父类的方法有多个,那么会出现异常,因为父类具有相同的匹配优先级,它不知如何处理。方法的重写:是动态分配,它和对象本身存在很大的联系,重写即是子类重写父类的方法,那么问题来了,哪些方法可以被子类重写了,答案就是非私有化的实例方法,重写的是方法体,但方法的句柄没有改变,也就是名称,参数,返回值,访问控制符没有改变,说的通俗点就是样子没变,灵魂变了。如果子类重写了父类的方法,那么该方法就和对象深深的紧密联系在一起。例如将子类的对象赋值给父类的引用时,只要父类调用被子类重写的方法,那么即使此时是父类的引用,正因为它是动态分配,所以,它的调用只和对象有关,与调用它的对象引用无关。属性的重写:属性的重写也可以近似看成是静态分配,它只和对象的引用有关,与对象无关。


                             七.泛型技术

7.1 泛型简介

    泛型一般只存在于编译期间,它能够使程序更加安全。泛型分为两种:方法泛型,类的泛型。

7.2 类的泛型

    class<T...vargs>

class  Generics<T,V,X>
{
public T t;
public V v;
public X x;
public  Generics(T t,V v,X x)
{
this.t=t;
this.v=v;
this.x=x;
}}
方法泛型:<T...vargs> T 方法名称(T...vargs)

public <T> T get(T t)
{
	return t;

}
嵌套泛型:

class Info<T,V>{
    private T var;
    private V value;
    public Info(T var,V value){
        this.setVar(var);
        this.setValue(value);
    }
    public T getVar() {
        return var;
    }
    public void setVar(T var) {
        this.var = var;
    }
    public V getValue() {
        return value;
    }
    public void setValue(V value) {
        this.value = value;
    }
    
}
class Demo<S>{
    private S info;
    public Demo(S info){
        this.setInfo(info);
    }
    public S getInfo() {
        return info;
    }
    public void setInfo(S info) {
        this.info = info;
    }
}
public class GenericsDemo {
        public static void main(String[] args) {
            Demo<Info<String,Integer>> d=null;
            Info<String,Integer> i=null;
            i=new Info<String,Integer>("Lixinghua",30);//注意此处
            d=new Demo<Info<String,Integer>>(i);//注意此处
            System.out.println("content1:"+d.getInfo().getVar()); 

            System.out.println("content2:"+d.getInfo().getValue());
        }}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值