一、类和包
封装一组类、子包和接口的机制,其可以被视为数据封装(或数据隐藏)
包的作用:
- 提供搜索和定位类、接口、枚举和注释等
- 防止命名冲突(包采用树形目录的存储方式,同包中类的名字不同,不同包中类的名字可以相同。)
- 访问控制(拥有包访问权限的类才能访问某个包中的类)
只有用import关键字导入相应包,才能够使用该包中的成员
常见包类型:
- java.lang:包含语言支持类(例如分类,用于定义基本数据类型,数学运算)。该软件包会自动导入。
- java.io:包含分类以支持输入/输出操作。
- java.util:包含实现像链接列表,字典和支持等数据结构的实用类; 用于日期/时间操作。
- java.applet:包含用于创建Applets的类。
- java.awt:包含用于实现图形用户界面组件的类(如按钮,菜单等)。
- java.net:包含支持网络操作的类。
二、.Java源文件相关知识点
- .java源文件中可以包括多个类
- .java源文件中只能有一个public类(编译器在编译时,针对一个.java文件只会接受一个public类,否则报错)
- .java文件中可以没有public类
- 包中public的类的类名必须和文件名相同(方便虚拟机在相应的路径中找到相应的类所对应的字节码文件)
- main方法一个特殊的函数,作为程序的入口,可被JVM调用
- (String[] args):main函数的参数,类型是一个数组,该数组中的元素为字符串数组。空数组的长度为0,但也可以在运行时向其中传入参数
- 一个java文件中可以包含多个类,每个类中有且仅有一个主函数,但是每个java文件中可以包含多个主函数,运行时,需指定JVM入口
- 外部类只能用public和default修饰(public 和 default 能区分该类能否被import;类被protected(仅同包内和子类可见)修饰,非同包类无法继承该包;private修饰的外部类,其他外部都无法import)
- 类没有修饰关键词,其默认为default修饰
三、构造方法
- 是类的一种特殊方法,用来初始化一个类的新对象
- Java每个类都有一个默认的构造方法,其必须和类名相同
- 没有返回类型,其默认的返回类型为对象类型本身
- 不能被 static、final、synchronized、abstract 和 native 修饰(原因:构造方法用于初始化一个新对象,所以用 static 修饰没有意义;构造方法不能被子类继承,所以用 final 和 abstract 修饰没有意义;多个线程不会同时创建内存地址相同的同一个对象,所以用 synchronized 修饰没有必要)
例1:
public class MyClass
{
MyClass() // 定义无参的构造方法
{
System.out.println("无参构造方法!")
}
MyCiass(int m) // 定义有参的构造方法
{
this.m=m;
}
}
在一个类中定义多个具有不同参数的同名方法,是方法的重载。在实例化该类时可以调用不同的构造方法进行初始化
类的构造方法不是要求必须定义的。如果在类中没有定义任何一个构造方法,则 Java 会自动为该类生成一个默认的构造方法。默认的构造方法不包含任何参数,并且方法体为空
例2:
不同的条件下使用不同的初始化行为创建类的对象,这时候就需要在一个类中创建多个构造方法
public class Worker
{
public String name; // 姓名
private int age; // 年龄
//定义带有一个参数的构造方法
public Worker(String name)
{
this.name=name;
}
//定义带有两个参数的构造方法
public Worker(String name,int age)
{
this.name=name;
this.age=age;
}
public String toString()
{
return"大家好!我是新来的员工,我叫"+name+",今年"+age+"岁。";
}
}
附:Object 类具有一个 toString() 方法,创建的每个类都会继承该方法。它返回一个 String 类型的字符串。如果一个类中定义了该方法,则在调用该类对象时,将会自动调用该类对象的 toString() 方法并返回一个字符串,然后使用“System.out.println(对象名)”就可以将返回的字符串内容打印出来。
四、构造方法详解
普通构造方法
- 方法名与类名相同
- 无返回类型
- 子类不能继承父类的构造方法
- 不能被static、final、abstract修饰(有final和static修饰的是不能被子类继承的,abstract修饰的是抽象类,抽象类是不能实例化的,也就是不能new)
- 可被private修饰,可以在本类里面实例化,但是外部不能实例化对象
默认构造方法
- 如果没有任何的构造方法,编译时系统会自动添加一个默认无参构造方法
重载构造方法
- 原本的类里的构造方法是一个参数的,现在新建的对象是有三个参数,此时就要重载构造方法
- 当一个类中有多个构造方法,有可能会出现重复性操作,这时可以用this语句调用其他的构造方法
例3:
public class A{
private int age;
private String name;
public A(int age,String name){
this.age=age;
this.name=name;
}
public A(int age){
this(age,"无名氏"); // 调用 A(int age,String name)构造方法
}
public A(){
this(1); // 调用 A(int age)构造方法
}
public void setName(String name) {this.name=name;}
public String getName() {return name;}
public void setAge(int age) {this.age=age;}
public int getAge() {return age;}
}
A a=new A(20,"周一");
A b=new A(20);
A c=new A();
String name = a.getName();
String name1 = b.getName();
int age = c.getAge();
System.out.println(name);
System.out.println(name1);
System.out.println(age);
五、子类构造方法调用父类构造方法
父类构造方法是绝不能被子类继承,所以子类构造方法只能通过调用来使用父类的构造方法
- 调用父类无参构造方法
- 当子类构造方法调用父类无参构造方法,一般都是默认不写的,要写的话就是super(),且要放在构造方法的第一句
- 当子类构造方法要调用父类有参数的构造方法,那么子类的构造方法中必须要用super(参数)来调用父类构造方法,且要放在构造方法的第一句
- 当子类的构造方法是无参构造方法时,必须调用父类无参构造方法。因为系统会自动找父类有没有无参构造方法,如果没有的话系统会报错
- 当子类构造方法是有参构造方法时,若其方法中没有写super,默认调用父类无参构造方法(与子类是无参的构造方法相同)
- 子类构造方法有super(参数)时,就调用父类有参构造方法,系统会找父类有没有参数一致(参数数量,且类型顺序要相同)的有参构造方法,如果没有的话,也会报错
六、代码块简介
普通代码块:
- 类中方法的方法体
构造代码块:
- 构造块会在创建对象时被调用,每次创建时都会被调用,优先于类构造函数执行
静态代码块:
- 用static{}包裹起来的代码片段,只会执行一次。静态代码块优先于构造块执行
同步代码块:
- 使用synchronized(){}包裹起来的代码块,在多线程环境下,对共享数据的读写操作是需要互斥进行的,否则会导致数据的不一致性。同步代码块需要写在方法中
静态代码块和构造代码块的异同点:
相同点:
- JVM加载类后且在构造函数执行之前执行,在类中可定义多个
不同点:
- 静态代码块在非静态代码块(构造代码块)之前执行
- 静态代码块只在第一次new时执行一次,之后不在执行,而非静态代码块(构造代码块)每new一次就执行一次
七、代码块详解
局部代码块
- 位置:局部位置(方法内部)
- 作用:限定变量的生命周期,尽早释放,节约内存
- 调用:调用其所在的方法时执行
例1:
class B {
B(){}
public void go() {
//方法中的局部代码块,一般进行一次性地调用,调用完立刻释放空间,避免在接下来的调用过程中占用栈空间
//因为栈空间内存是有限的,方法调用可能会会生成很多局部变量导致栈内存不足。
//使用局部代码块可以避免这样的情况发生。
{
int i = 1;
ArrayList<Integer> list = new ArrayList<>();
while (i < 1000) {
list.add(i ++);
}
for (Integer j : list) {
System.out.println(j);
}
System.out.println("gogogo");
}
System.out.println("hello");
}
}
构造代码块
- 位置:类成员的位置,就是类中方法之外的位置
- 作用:把多个构造方法共同的部分提取出来,共用构造代码块
- 调用:每次调用构造方法时,都会优先于构造方法执行,也就是每次new一个对象时自动调用,对对象的初始化
例2:
public class CodeBlock {
int m = 1;
int initValue ;//成员变量的初始化交给代码块来完成
public CodeBlock() {
System.out.println("构造器");
}
{
//代码块的作用体现于此:在调用构造方法之前,用某段代码对成员变量进行初始化。
//而不是在构造方法调用时再进行。一般用于将构造方法的相同部分提取出来。
//
for (int m = 1; m < 101;m ++) {
initValue += m;
}
}
{
System.out.println(initValue);
System.out.println(m);//此时会打印1
int m = 2;//代码块里的变量和成员变量不冲突,但会优先使用代码块的变量
System.out.println(m);//此时打印2
}
int j = 2;
{
System.out.println(j);
System.out.println(m);//代码块中的变量运行后自动释放,不会影响代码块之外的代码
}
{
System.out.println("代码块运行");
}
public static void main(String[] args) {
CodeBlock cb = new CodeBlock();
}
}
public class CodeBlock {
//构造代码块
{
System.out.println("构造代码块");
}
//静态构造代码块
static{
System.out.println("静态构造代码块");
}
//构造器
public CodeBlock() {
System.out.println("构造器");
}
public static void main(String[] args) {
//两次创建对象
CodeBlock a = new CodeBlock();
CodeBlock b = new CodeBlock();
}
静态代码块
- 位置:类成员的位置,用static修饰的代码块
- 作用:对类进行初始化,当new多个对象时,只能加载第一个new对象,执行一次
- 调用:new对象时自动调用,并只能调用一次
例3:
public class CodeBlock {
//构造器
public CodeBlock() {
System.out.println("构造器");
}
//构造代码块
{
System.out.println("构造代码块");
}
//静态构造代码块
static{
System.out.println("静态构造代码块");
}
public static void main(String[] args) {
//两次创建对象
CodeBlock a = new CodeBlock();
CodeBlock b = new CodeBlock();
}
}
结果输出:
八、代码块、构造方法(包含继承关系)的执行顺序
父类的静态成员和代码块——>子类的静态成员和代码块——>父类成员初始化和代码块——>父类构造方法——>子类成员初始化和代码块——>子类构造方法
例1:
//A类
public class A {
static {
Log.i("HIDETAG", "A静态代码块");
}
private static C c = new C("A静态成员");
private C c1 = new C("A成员");
{
Log.i("HIDETAG", "A代码块");
}
static {
Log.i("HIDETAG", "A静态代码块2");
}
public A() {
Log.i("HIDETAG", "A构造方法");
}
}
//B类
public class B extends A {
private static C c1 = new C("B静态成员");
{
Log.i("HIDETAG", "B代码块");
}
private C c = new C("B成员");
static {
Log.i("HIDETAG", "B静态代码块2");
}
static {
Log.i("HIDETAG", "B静态代码块");
}
public B() {
Log.i("HIDETAG", "B构造方法");
}
}
//C类
public class C {
public C(String str) {
Log.i("HIDETAG", str + "构造方法");
}
}
// 主函数
public static void main(String[] args) {
B a = new B();
}
输出结果:
参考文章:
Java-Tutorial/7、代码块和代码执行顺序.md at master · h2pl/Java-Tutorial · GitHub