安阳建设网站哪家好,a做爰视频免费网站,免费移动网站模板下载安装,科技公司注册需要什么条件文章目录 第1章 Java基础知识1.1 变量的初始化顺序1.2 构造方法1.3 clone()方法1.3.1 按值传递和按引用传递1.3.2 浅拷贝#xff08;Shallow Clone#xff09;1.3.3 深拷贝#xff08;Deep Clone#xff09; 第1章 Java基础知识
1.1 变量的初始化顺序
在Java语言中#… 文章目录 第1章 Java基础知识1.1 变量的初始化顺序1.2 构造方法1.3 clone()方法1.3.1 按值传递和按引用传递1.3.2 浅拷贝Shallow Clone1.3.3 深拷贝Deep Clone 第1章 Java基础知识
1.1 变量的初始化顺序
在Java语言中变量一定要初始化吗
第一如果变量作为局部变量也就是方法内定义的变量或者作为方法参数的变量在使用前一定要初始化。 例如
public static void main(String[] args) {int a;String b;System.out.println(a);System.out.println(b);
}执行该main()方法控制台报错
java: 可能尚未初始化变量a第二如果变量作为一个类的属性没有初始化时JVM 会自动把它初始化为该类型变量的默认初始值。
如int类型的变量默认初始值为0float类型的变量默认初始值为0.0flong 类型为0double类型为 0.0boolean类型为falsechar类型为0ASCII 码所有引用类型包括数组的变量默认初始值为null。
在Java语言中当要实例化一个对象时首先要初始化所有成员变量包括静态和非静态变量只有当所有成员变量完成初始化后才会调用对象所在类的构造方法创建对象。
成员变量的初始化一般遵循以下三个原则优先级依次递减
1静态变量优先于非静态变量初始化其中静态变量只初始化一次而非静态变量可能会初始化多次。 2父类优先于子类进行初始化。 3按照成员变量定义的顺序进行初始化且在任意方法包括构造方法被调用之前先进行初始化。
成员变量的初始化工作可以在许多不同的代码块中来完成比较常见的是静态代码块和构造方法。
静态代码块和构造方法的执行顺序是父类静态代码块→子类静态代码块→父类非静态代码块→父类构造方法→子类非静态代码块→子类构造方法。
下面看一个例子首先新建一个Animal类并编写静态代码块、非静态代码块和构造方法
public class Animal {static {System.out.println(Animal类的静态代码块执行了...);}{System.out.println(Animal类的非静态代码块执行了...);}public Animal() {System.out.println(Animal类的构造方法执行了...);}
}在编写一个Dog类继承Animal类也编写静态代码块、非静态代码块和构造方法
public class Dog extends Animal {static {System.out.println(Dog类的静态代码块执行了...);}{System.out.println(Dog类的非静态代码块执行了...);}public Dog() {System.out.println(Dog类的构造方法执行了...);}public static void main(String[] args) {new Dog();}
}执行Dog类中的main()方法调用Dog类的构造方法。控制台打印出来的执行顺序跟预期一致
Animal类的静态代码块执行了...
Dog类的静态代码块执行了...
Animal类的非静态代码块执行了...
Animal类的构造方法执行了...
Dog类的非静态代码块执行了...
Dog类的构造方法执行了...需要注意的是静态变量在定义在直接初始化和使用静态代码块进行初始化的优先级是一样的也就是按照从上往下的顺序进行初始化。这一结论也适用于非静态变量和非静态代码块。
例如
public class Cat {static int age 1;static {age 2;}static {name Jerry;}static String name Tom;public static void main(String[] args) {// 按照从上往下的顺序进行初始化// 最终age2nameTomSystem.out.println(age);System.out.println(name);}
}控制台输出结果
2
Tom1.2 构造方法
在Java语言中构造方法是一种特殊的方法主要作用是完成对象的初始化工作。它具有以下特点
1构造方法的名称必须与类的名称相同且不能有返回值void也不行但可以有0个、1个或1个以上的参数。2构造方法可以被重载Overload即每个类可以有多个构造方法使用不同的参数个数或参数类型来定义多个构造方法。3当一个类没有定义构造方法时编译器在把源代码编译成字节码的过程中会提供一个默认的没有参数的构造方法但该构造方法不会执行任何代码。如果定义了则不会再创建。4构造方法在对象实例化时会被自动调用。对于一个对象而言只会被调用一次而普通的方法可以被调用多次。5构造方法不能被继承因此不能被重写Override子类可以通过super关键字来显式地调用父类的构造方法。6当父类没有提供无参数的构造方法时子类的构造方法中必须显式地调用父类的构造方法如果父类提供了无参数的构造方法子类就可以不显式地调用父类的构造方法这种情况下编译器会默认调用父类的无参数构造方法。7在实例化对象时会首先调用父类的构造方法再执行子类的构造方法。8默认构造方法的修饰符只跟当前类的修饰符有关例如一个类被定义为public则其构造方法也是public。
引申一个问题普通方法是否可以与构造方法有相同的方法名
答案是可以的。 例如
public class Dog {public Dog() {System.out.println(Dog类的构造方法执行了...);}public void Dog() {System.out.println(Dog类的Dog()方法...);}public static void main(String[] args) {Dog dog new Dog();dog.Dog();}
}程序运行结果为
Dog类的构造方法执行了...
Dog类的Dog()方法...1.3 clone()方法
1.3.1 按值传递和按引用传递
在Java语言中取消了C/C语言中“指针”的概念但实质上每个new语句返回的都是一个指针的引用只是大部分情况下开发人员不需要关心如何去操作这个指针而已。
下面先看一个例子
public class Book {private String name 《Java程序员》;// getter setter ...
}public class User {private Book book new Book();private int age 19;// getter setter ...public void changeBook(Book book) {book.setName(《三体1地球往事》);}public void changeAge(int age) {age 1;}public static void main(String[] args) {User user new User();System.out.println(********引用类型********);System.out.println(调用changeBook()前 user.getBook());user.changeBook(user.getBook());System.out.println(调用changeBook()后 user.getBook());System.out.println(********基本数据类型********);System.out.println(调用changeAge()前 user.getAge());user.changeAge(user.getAge());System.out.println(调用changeAge()前 user.getAge());}}上述代码的执行结果
********引用类型********
调用changeBook()前《Java程序员》
调用changeBook()后《三体1地球往事》
********基本数据类型********
调用changeAge()前19
调用changeAge()前19Java在处理基本数据类型时例如int、char、double等都是采用按值传递的方式传递的是输入参数的拷贝除此之外的其他类型都是按引用传递的方式传递的是对象的一个引用。
因此在上述示例中changeBook()方法的参数是Book对象的一个引用因此修改Book对象的属性并不会影响User对象持有它而changeAge()方法的参数真的就只是一个数这个数怎么变跟User对象的age属性无关除非使用this关键字进行关联例如
public void changeAge(int age) {// 不会修改User对象的age属性age 1;// 会修改User对象的age属性this.age 2;
}对象除了在函数调用时是引用传递在使用赋值时也采用引用传递。 例如
public static void main(String[] args) {Book bookA new Book();System.out.println(bookA修改前: bookA.getName());// 将bookA的引用赋值给bookBBook bookB bookA;System.out.println(bookB修改前: bookB.getName());// 修改bookB的信息bookB.setName(《三体1地球往事》);// bookA的信息也会修改System.out.println(bookA修改后: bookA.getName());System.out.println(bookB修改后: bookB.getName());
}上述代码的执行结果
bookA修改前:《Java程序员》
bookB修改前:《Java程序员》
bookA修改后:《三体1地球往事》
bookB修改后:《三体1地球往事》1.3.2 浅拷贝Shallow Clone
在实际编程中经常会遇到从某个已知的对象A创建出另外一个与A具有相应状态的对象B并且要求对B的修改不会影响到A的状态。
在Java语言中仅通过简单的赋值操作显然无法达到这个目的但Java提供了一个简单且有效的clone()方法来满足这个需求。
Java中所有的类都默认继承自Object类而Object类中提供了一个clone()方法用于返回一个Object对象的拷贝这个拷贝是一个新的对象而不是原对象的引用。
使用clone()方法的步骤如下
1要实现了clone()方法的类首先要实现Cloneable接口。Cloneable接口实质上只是一个标识接口没有定义任何接口方法。2在类中重写Object类的clone()方法调用super.clone()方法。该方法得到的实际上是一个浅拷贝对象。
public class Book implements Cloneable {private String name 《Java程序员》;// getter setter ...Overrideprotected Book clone() throws CloneNotSupportedException {return (Book)super.clone();}public static void main(String[] args) throws CloneNotSupportedException {Book bookA new Book();System.out.println(bookA修改前: bookA.getName());// 调用clone()方法得到一个新的对象Book bookB bookA.clone();System.out.println(bookB修改前: bookB.getName());bookB.setName(《三体1地球往事》);// bookA对象不受影响System.out.println(bookA修改后: bookA.getName());System.out.println(bookB修改后: bookB.getName());}
}上述代码的执行结果
bookA修改前:《Java程序员》
bookB修改前:《Java程序员》
bookA修改后:《Java程序员》
bookB修改后:《三体1地球往事》可见此时对bookB对象的修改已不会影响bookA对象。
1.3.3 深拷贝Deep Clone
要注意的是Java在重载clone()方法的时候也存在浅拷贝、深拷贝的问题。当类中只有一些基本的数据类型时采用上述方法进行浅拷贝就可以了但是当类中包含一些对象时就需要用到深拷贝。例如Book对象中还有一个Date类型的属性
public class Book implements Cloneable {private String name 《Java程序员》;private Date birthday new Date();// getter setter ...Overrideprotected Book clone() throws CloneNotSupportedException {return (Book)super.clone();}public static void main(String[] args) throws CloneNotSupportedException {Book bookA new Book();System.out.println(bookA修改前: bookA.getName() , bookA.getBirthday());Book bookB bookA.clone();System.out.println(bookB修改前: bookB.getName() , bookB.getBirthday());bookB.setName(《三体1地球往事》);bookB.getBirthday().setMonth(5);System.out.println(bookA修改后: bookA.getName() , bookA.getBirthday());System.out.println(bookB修改后: bookB.getName() , bookB.getBirthday());}
}上述代码的执行结果
bookA修改前:《Java程序员》, Sun Mar 31 10:45:13 CST 2024
bookB修改前:《Java程序员》, Sun Mar 31 10:45:13 CST 2024
bookA修改后:《Java程序员》, Mon Jul 01 10:45:13 CST 2024
bookB修改后:《三体1地球往事》, Mon Jul 01 10:45:13 CST 2024可见修改bookB对象的Dete类型属性时会影响到bookA。
深拷贝的实现方法是在对象调用clone()方法完成浅拷贝后再对非基本类型属性也调用clone()方法完成深拷贝。 例如上述示例的clone()方法改成这样
Override
protected Book clone() throws CloneNotSupportedException {Book book (Book) super.clone();// 单独对Date类型的属性进行深拷贝book.setBirthday((Date) this.getBirthday().clone());return book;
}再次执行main()方法执行结果为
bookA修改前:《Java程序员》, Sun Mar 31 10:56:09 CST 2024
bookB修改前:《Java程序员》, Sun Mar 31 10:56:09 CST 2024
bookA修改后:《Java程序员》, Sun Mar 31 10:56:09 CST 2024
bookB修改后:《三体1地球往事》, Mon Jul 01 10:56:09 CST 2024可见此时bookA对象的Date属性值是不变的也即完成了深拷贝。
总结一下在实际编程的时候要先检查类中有无非基本类型即对象的属性如果没有使用浅拷贝即可如果有则需要在完成浅拷贝后对每一个非基本类型的属性进行深拷贝。
…
本节完更多内容请查阅分类专栏再探Java为面试赋能(持续更新中)
感兴趣的读者还可以查阅我的另外几个专栏
SpringBoot源码解读与原理分析(已完结)MyBatis3源码深度解析(已完结)Redis从入门到精通(持续更新中)