Coursera PKU Java 程序设计学习笔记 #1( 基础部分 )

这篇博客详细记录了Java程序设计的基础部分,包括表达式、变量传递、多态、对象构造与初始化、垃圾回收、内部类与匿名类、Lambda表达式、异常处理等核心概念。通过对JVM指令码的反汇编,加深了对Java运算符优先级和类型转换的理解。此外,还探讨了Java中的多态特性,强调了虚方法调用和上溯造型的概念。在对象构造过程中,解释了构造方法的执行流程,并讨论了构造方法中调用其他方法的风险。最后,提到了Java的垃圾回收机制,以及内部类和匿名类的定义与使用。
摘要由CSDN通过智能技术生成

Java 程序设计基础部分笔记

表达式以及反汇编

表达式

表达式: 符合一定语法规则的运算符和操作数序列。

表达式中的运算符优先级
Separator. ( ) { } ; ,
AssociativeOperators
R to L++ – ~ ! (data type)
L to R* / %
L to R+ -
L to R<< >> >>>
L to R< > <= >= instanceof
L to R== !=
L to R&
L to R^
L to R|
R to L?:
R to L= *= /= %= += -= <<= >>= >>>= &= ^= !=

tips: 尽量使用括号提升代码的可读性

表达式中的类型转换

整型提升: 所有参与算术运算的 byte 、 short 、 char 类型都将被自动转换为 int 型;
混合运算自动转换:int -> long -> float -> double

反汇编

使用 JDK 提供的 javap 反编译 java 文件可查看 JVM虚拟机指令码

javap -c 类名

变量及其传递

变量

基本类型与引用类型
  • 基本类型 ( Primitive type ):其值直接存于变量中。“ 在这里 ”

  • 引用类型( Reference type ):变量除占据一定的内存空间外,它所引用的对象实体(由 new 创建 )也要占据一定空间。“ 在那里 ”

字段变量和局部变量
  • 字段变量(域):字段变量和对象实例一同存在于 Java Heap 中,自动赋初值

字段变量属于类,可以用 public 、 private 、 static 、 final 修饰。

  • 局部变量:存在于虚拟机栈(方法栈帧)中,需要显式的赋值 。

局部变量不能够被访问控制符以及 static 修饰。
可以被 final 修饰 ;
final 修饰局部变量的情况:
形式参数获取引用型变量时,避免引用赋值。

变量的传递

  • 调用对象方法时,要传递参数。在传递参数时,Java是值传递,即:将表达式的值复制给形式参数。
  • 对于引用型变量,传递的值是引用值(指针/地址/句柄),而不是对象实体。

tips : 即可以通过引用改变对象的属性。

// main ( String ) :
int a = 0 ; 
modify( a ); 
System.out.println(a);	// result: 0

int[] b = new int[1];
modify( b );
System.out.println(b[0]);	// result: 1

// modify( int a )
a++ ;

//modify( int[] b )
b[0]++;
b = new int[1]; 

多态以及虚方法调用

多态的两种情形

编译时的多态
  • 重载 ( overload ) 多个同名的不同方法。
  • 如 不同参数列表的构造方法。
运行时的多态
  • 覆盖 ( override ) 子类对父类方法进行覆盖
  • 动态绑定 ( dynamic binding ) — 虚方法调用 ( virtual method invoking )。
  • 在调用方法时,程序会正确的调用子类对象的方法。

多态的特点大大提高了程序的抽象性和简洁性

上溯造型( upcasting )

可以把派生类型当作基本类型处理

Person p = new Student();

void fun ( Person p ) {...}
fun( new Student() );  
虚方法调用

关于虚方法调用的简记(Java Core 阅读笔记 Part.1)

非虚方法的调用
  • 在 Java 中,普通的方法是虚方法。
  • 但 static 、private 方法不是虚方法调用;
  • static、private 与虚方法编译后用的指令是不同的。

演示:

class JavaP3methods 
{
	void f(){}
	private void p(){}
	static void s(){}
	public static void main ( String[] args )
	{
		JavaP3methods obj = new JavaP3methods();
		obj.f();
		obj.p();
		obj.s();
	}
}

虚拟机指令:

public static void main (java.lang.String...);
	Code:
		0:new				#2		// class JavaP3methods
		3:dup
		4:invokespecial		#3		// Method "<init>":()V
		7:astore_1
		8:aload_1
		9:inbokevirtual		#4		// Method f:()V
		12:aload_1
		13:invokespecial	#5		// Method p:()V
		16:aload_1
		17:pop
		18:invokestatic		#6		// Method s:()V
		21:return

对象构造和与初始化

构造方法

调用本类或父类的构造方法
  • this 调用本类的构造方法
  • super 调用父类的构造方法
  • this 或 super 要放在第一条语句,且只能够第一条

如果没有 this 及 super ,则编译器会自动加上 super() ,即直接调用父类不带参数的构造方法。
因为必须令所有父类的构造方法都得到调用,否则整个对象的构建就不可能正确。

一个问题
class A
{
	A( int a ){}
}
class B extends A 
{
	B( String s ){}	//编译不能通过
}
  • 编译器会自动调用 B( String s ) { Super(); } 出错。
  • 解决方法:
  1. 在 B 的构造方法中,加入 super(3);
  2. 在 A 中加入一个不带参的构造方法,A() {} ;
  3. 去掉 A 中全部构造方法,则编译器会自动添加无参构造方法( default construct )。

创建对象时初始化语法

p = new Person() {{ age = 18; name = "李明"; }};

实例初始化与静态初始化

  • 实例初始化( Instance Initializers )
    在类中直接写
class Test
{
	// Filed 
	
	{ Coding ... }
	
	// Methods
}

实例初始化,优先与构造方法 {} 中的语句执行。

  • 静态初始化( Static Initializers )
class Test
{
	// Filed 
	
	static { Coding ... }
	
	// Methods
}

静态初始化,在构建类(第一次使用)时执行。
tips : 但其实是不准确的,可以肯定的是优先于实例初始化

构造过程

构造方法的执行过程遵照以下几个步骤
  • 调用本类或父类的构造方法,直至最高一层( Object )
  • 按照声明顺序执行字段的初始化赋值
  • 执行构造函数中的各语句

简单来说:先父类构造,在本类成员赋值,最后执行构造方法中的语句。

class Person 
{
	String name = "unknown" ;	// step 2
	int age = -1 ; 
	
	Person ( String name , int age )
	{
		super(); // step 1 
 		
 		// step 3 
 		System.out.println( "开始构造 Person() ,此时 this.name = "+this.name+" this.age = "+this.age);
 		this.name = name ;
 		this.age = age ;
 		System.out.println( " Person() 构造完成,此时 this.name = "+this.name+" this.age = "+this.age);
	}
}

/*
main :
	p = new Person ( "XiaoMing" , 18 );

result :

开始构造 Person() ,此时 this.name = unknown this.age = -1 ;
Person() 构造完成,此时 this.name = XiaoMing , this.age = 18 ;

*/
一个问题
  • 构造方法内调用别的方法,且还是虚方法,结果如何。

语法上来说是合法的,但有时会造成事实上的不合理。

class Person 
{
	String name ;
	int age ;
	Person ( String name , int age )
	{
		this.name = name ;
		this.age = age ;
		sayHello();
	}
	
	void sayHello ()
	{
		System.out.println(" Hello ")
	}
}

class Student extends Person
{
	String shcool ;
	Student( String name , int age )
	{
		Person( name , age );
	}
	@Override
	void sayHello ()
	{
		System.out.println( " Hello , My name is  "+this.name+" I am ..... Study in "+this.shcool );
	}
}

//main():
Person p = new Student( "Jack" , 20 );

// result :
Hello , My name is  Jack I am ..... Study in null

在本例中,在构造函数中调用了一个动态绑定的方法 sayHello(),这时,会使用那个方法被覆盖的定义,而这时对象尚未完全建好,所以 school 还没赋值。
因此,可能的话,在构建方法中避免使用任何方法,用尽可能简单的方法使对象进入就绪状态。
惟一能够安全调用的是具有final 属性的方法。

对象清除和垃圾回收

对象清除

Java 拥有自己的垃圾回收机制,不需要使用 delete 销毁对象( 相比于C )。

对象的自动清除

  • 垃圾回收( Garbage Collection )。
  • 对象回收是由 JVM 的垃圾回收线程来完成的。
  • 任何对象都有一个引用计数器,当其值为 0 时,说明该对象可以回收。
  • System.gc() 方法,建议系统对某对象进行回收,仅仅是 “ 建议( suggest )”。

finalize () 方法

  • Java 中不提供 destructor 析构方法。
  • Object 中拥有 finalize() 方法,系统会在回收时自动调用对象的 finalize() 方法。
class Object
{
...
	protected void finalize() throws Throwable{}
}
  • 子类可以 Override finalize() 方法释放系统资源
  • 一般来说,子类的 finalize() 方法中应该调用父类的 finalize() 方法,以保证父类的清理工作能够正常的执行。

Try-with-resource (JDK 1.7)

  • 由于 finalize() 方法的调用时机并不确定,所以一般不用 finalize() ;
  • 关闭打开的文件,清除一些非内存资源等工作需要进行处理。可以使用 try-with-resource语句
  • 对于实现了 java.lang.AutoCloseable 的对象:
try ( Scanner sc = new Scanner( System.in ) )
{
	used sc ...
}

会自动调用其 close 方法,相当于:

finally
{
	sc.close();
}

内部类和匿名类

内部类

内部类( Inner Class)的定义和使用
内部类的定义
  • 将类的定义 class xx { … } 置于一个类的内部。
  • 编译器生成 xxxx$xxxx 这样的 class 文件。
  • 内部类不能够与外部类同名
内部类的使用
  • 在封装它的类的内部使用内部类,与普通类的使用方式相同。
  • 在其他地方使用:
    在类名前要冠以外部类的名字。
    在 new 创建内部类时,也要在 new 前冠以外部对象变量。
class Parcel 
{
	private Contents c ; 
	private Destination d;
	class Contents 
	{
		private int i ;
		Contents ( int i ) { this.i = i }
		int value() { return i ; }
	}

	class Destination 
	{
		private String label;
		Destination( String whereTo ) { label = whereTo; }
		String readLabel() { return label ; }
	}
	
	void setProperty( Contents c ; Destination d )
	{
		this.c = c ;
		this.d = d ;
	}
	
	void ship()
	{
		System.out.println( " move "+ c.value() + " to "+ d.readLabel() );
	}

	public void testShip()
	{
		c = new Contents(22);
		d = new Destination("Beijiing");
		ship();
	}
}


// 在外部使用内部类

class TestInnerClass
{
	public static void main ( String[] args )
	{
		Parcel p = new Parcel();
		p.testShip();

		Parcel.Contents c = p.new Contents(33);
		Parcel.Destination d = p.new Destination("Hawii");
		p.setProperty( c , d );
		p.ship();
	}
}
在内部类中使用外部类的成员
  • 内部类可以直接访问外部类的字段和方法( private 也行)。
  • 如果内部类中有和外部类同名的字段和方法,则可以用:外部类名.this.字段以及方法 ; 。
class A
{
	private int s = 111 ;
	
	public class B 
	{
		private int s = 222 ;
		public void test ( int s )
		{
			int a = s ;			// 局部变量 s
			int b = this.s ;	// 内部类的域 s 
			int c = A.this.s ;	// 外部类的域 s
		}
	}
}
内部类的修饰符
  • 内部类与类中的字段,方法一样是外部类的成员,它的前面可以有访问控制符和其他修饰符。
  • 访问控制符 private 、public、protected
  • static 、 final

嵌套类

  • 使用 static 修饰符修饰内部类( Inner Class ),表明该内部类实际是一种外部类。
  • 使用 static 修饰符修饰的内部类称之为嵌套类( Nested Class ),不是内部类。
嵌套类在使用时
  1. 实例化 static 类时,在 new 前面不需要冠以外部对象实例变量;
  2. static 类中不能访问其外部类的非 static 字段和方法,即只能访问 static 成员;
  3. static 方法不能访问非 static 的字段以及方法,也不能够不带前缀的 new 一个非 static 的内部类;
class Outer
{
	static class Inner
	{
	}
}

class TestInnerStatic
{
	public static void main ( String [] args )
	{
		Outer.Inner oi = new Outer.Inner();
		Outer.Inner oi2 = Outer.new Inner();			//!!! error
		Outer.Inner oi3 = new Outer().new Inner();		//!!! error
	}
}

局部类以及匿名类

局部类( Local Class )
局部类的定义
  • 在一个方法中定义的内部类,称之为局部类。
class Outer
{
	private int size = 5 ;
	public Object makeTheInner ( int localVar )
	{
		final int finalLocalVar = 99 ;
		class Inner
		{
			@Override
			public String toString()
			{
				return ( " InnerSize : "+ size +
				// " localVar : " + localVar // Error!
				" finalLocalVar : "+finalLocalVar );
			}
			return new Inner();
		}
	}
}
局部类的使用
  1. 同局部变量一样,方法中的内部类,不能够用访问修饰符以及 static 修饰。
  2. 但可以被 final 或者 abstract 修饰。
  3. 可以访问其他外部成员。
  4. 不能够访问局部变量,除非是 final 局部变量。
匿名类( Anonymous class )
匿名类的定义
  • 匿名是一种特殊的匿名类,它没有类名,在定义时生成该对象的实例,实现 “ 一次性使用 ” 的类。
class Outer 
{
	private int size = 5 ;
	public Object makeTheInner ( int localVar )
	{
		final int finalLocalVar = 99 ;
		return new Object () 
		{
			@Override 
			public String toString() 
			{
				return ( "InnerSize:"+ size + " finalLocalVar:"+ finalLocalVar );
			}
		}
	}
}
匿名类的使用
  1. 不取名字,直接用父类或实现接口的名字。
  2. 编译器生成 xxxxxx$1之类的名字
  3. 类定义的同时就创建了实例,即类的定义前就有一个 new
  4. 在构造对象时使用父类的构造方法

Lambda 表达式(Java 8)*

Lambda 表达式的写法

  • 由参数获得结果
( 参数 ) -> 结果

//如

( String s ) -> s.length() ;

x -> x*x ;

() -> { System.out.println("aaaa"); }

  • 大体相当于其他语言的 “匿名函数” 或 ”函数指针“;
  • 在 Java 中,它实际上是 ”匿名函数的一个实例“;

Lambda 表达式是接口、接口函数的简写。
实际上,把 输入参数获得结果的过程抽象为 lambda 表达,作为接口实现使用。

Lambda 表达式的条件

  • lambda 表达式只能表示一个函数
  • 能写成 lambda 的接口要求包含且最多只能有一个抽象函数
  • 这样的借口可以用注记 @FunctionalInterface 来表示,称为函数式接口。

lambda 表达式,函数式编程:代码当作数据。

其他几个高级语法

装箱

基本类型和包装类
  • 它将基本类型( primitive )包装为引用类型( Object )
  • int -> Integer
  • 共 8 类 : Boolean , Byte , Short , Character , Integer , Long , Float , Double .
Integer i = new Integer(10);
装箱和拆箱
装箱( Boxing )
Integer I = 10 ;

// 自动装箱
// 编译器实际自动编译为

Integer i = Integer.valueOf(10);

拆箱( Unboxing )
int i = I ;

// 自动拆箱
// 编译器实际自动编译为

int i = i.iniValue();

主要方便用于集合中:

Object[] ary = { 1 ,  "aaa" };
//实际上是
Object[] ary = { Integer.valueOf(1) , new StringBuilder("aaa") };

枚举

  • 是一种特殊的 class 类型
enum Light { Red , Yellow , Green , Pink };
Light light = Light.Red
  • 实际生成了 class Light extends java.lang.Enum

枚举可以添加字段、方法、构造方法。

注解

  • 在语法要素上添加附加信息,以供编辑器或者其他程序使用。

所有注解都是 java.lang.annotation.Annotation 的子类。

  • 常用的注解,如
 @Override  		//表示覆盖了父类的方法
 @Deprecated 		//表示过时的方法
 @SuppressWarnings 	//表示让编译器不产生警告
自定义注解
public @interface Author
{
	String name();
}
// 比较复杂,详情。

没有指针的 Java 语言

引用与指针

  • 引用( Reference )实质就是指针( Pointer )。
  • 但它是受控的、安全的;
  • 比如:
    会检查空指引,
    没有指针运算,
    不能访问没有引用到的内存,
    自动回收垃圾。

相等还是不等

基本类型的相等
  • 数值相等:转换后比较(向上塑型)
  • 浮点数,最好不要用 ==
  • Double.NAN == Double.NAN // false API有注解
  • boolean 不能与 int 相比较。
Integer i = new Integer(10);
Integer j = new Integer(10);
System.out.println( i == j ); // false

Integer m = 10 ;
Integer n = 10 ;
System.out.println( m == n ); // true 
/*
实际上的代码
Integer m = Integer.valueOf(10);
Integer n = Integer.valueOf(10);

Java 将-128 支 127 数缓存起来返回,所以 m 和 n 指向的是同一对象。
*/

Integer p = 200 ;
Integer q = 200 ;
System.out.println( p == q ); // false 因为是两个对象

枚举、引用对象是否相等
枚举类型

内部进行了惟一实例化,所以可以直接用 == 判断。

引用对象
  • 是直接看两个引用是否一样。
  • 如果要判断内容是否一样,则要重写 equals 方法。
  • 如果重写 equals 方法,则最好重写 hashCode() 方法。
String 对象的特殊性
  • 判断相等,一定不要用 == ,要用 equals 判断内容是否相等。
  • 但是字符串常量( String literal )及字符串常量会进行内部化( interned ),相通的字符串常量是 == 的。

如果两个字符串常量是一样的,那么它们都将会被引用到同一个字符串常量上去。

"test" // 字符串常量 final String string = "test";

String hello = "Hello", lo = "lo";

System.out.println( hello == "Hello" ); 	// true
System.out.println( Other.hello = hello ); 	// true

System.out.println( hello == ( "Hel"+"lo" ) ); 	// true
System.out.println( hello == ( "Hel"+lo ) ); 	// false

System.out.println( hello == new String("Hello") ); 	// false
System.out.println( hello == ( "Hel"+lo.intern() ) );	// true

异常

Java 抛出和捕获

  1. 在某些地方抛出( throws )异常。
  2. JVM 在运行时在调用栈中查找(从生成异常的方法开始进行回溯,直到找到);
  3. 捕获( catch )异常的代码。
Exception 类
  • 构造方法
 - public Exception();
 - public Exception( String message );
 - Exception( String message , Throwable cause );
  • 方法
- getMessage();
- getCause();
- printStackTrace();
多异常的处理
  • 子类异常要在排子类异常前
  • finally 语句
    无论是否有异常都要执行:
    即使其中有 break , return 等语句;
    在编译时,finally 部分代码生成了多遍。

受检异常

RuntimeException
  • 运行时异常,可以不明确用语法处理,在运行时抛出。
CheckedException
  • 受检的异常,要求明确的进行语法处理
  • 要么捕获( catch )。
  • 要么抛出( throws ):在方法签名后用 throws xxx 声明。

tips ; 在子类中,如果要覆盖父类的一个方法,若父类中的方法声明了 throws 异常,则子类的方法也可以 throws 异常
可以抛出子类异常(更具体的异常),但不能抛出更一般的异常。

断言以及程序测试

Assert ( 不建议使用 )

assert 表达式;
assert 表达式 : 信息;

// 在调试程序时,如表达式不为 true ,则会产生异常,并输出相关的错误信息。

tips : 使用断言调试时,须在编译时加上 -source 源程序版本作为参数。
在运行时,需启用(-enableassertions)

程序测试工具

程序错误分类
  • 语法错误( Syntax error )IDE、编译器发现;
  • 运行错误( Runtime error )异常处理机制;
  • 逻辑错误( Logic error )debug模式、单元测试工具;
程序调试手段
  • 断点( breakpoint )
  • 跟踪( trace )
  • 监视( watch )

一般 IDE 提供调试模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值