在Java语言中,当实例化对象时,对象所在类的所有成员变量首先要进行初始化,只有当所有类成员完成初始化后,才会调用对象所在类的构造函数创建对象。
实例:
class Base {
// 1.父类静态代码块
static {
System.out.println("Base static block!");
}
// 3.父类非静态代码块
{
System.out.println("Base block");
}
// 4.父类构造器
public Base() {
System.out.println("Base constructor!");
}
}
public class Derived extends Base {
// 2.子类静态代码块
static{
System.out.println("Derived static block!");
}
// 5.子类非静态代码块
{
System.out.println("Derived block!");
}
// 6.子类构造器
public Derived() {
System.out.println("Derived constructor!");
}
public static void main(String[] args) {
new Derived();
}
}
输出结果:
Base static block!
Derived static block!
Base block
Base constructor!
Derived block!
Derived constructor!
反射(Reflection)是Java程序开发语言的特征之一,它允许运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性。通过Class 获取class 信息称之为反射。
简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。
程序中一般的对象的类型都是在编译期就确定下来,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知 的。所有我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
Java 反射框架主要提供以下功能:
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)
4.在运行时调用任意一个对象的方法
重点:是运行时而不是编译时
很多人都认为反射在实际的Java开发应用中并不广泛,其实不然。
当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法,这里就会用到反射。
反射最重要的用途就是开发各种通用框架
很多框架(比如 spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射-----运行时动态加载需要加载的对象。
对于框架开发人员来说,反射虽小但作用非常大,它是各容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。
1.调用运行时类本身的 .class属性
Class class1= Person.class;
System.out.println(class1.getName());
2.通过运行时类的对象获取 getClass();
Person p= new Person();
Class class2= p.getClass();
System.out.println(class2.getName());
3.使用Class 类的forName 静态方法
public static Class> forName(String className)
//在JDBC开发中常用此方法加载数据库驱动;
Class.forName(driver);
4.(了解)通过类的加载器ClassLoader
ClassLoader classLoader=this.getClas().getClassLoader();
Class class3=classLoader.loadClass(className);
System.out.println(class3.getName());
Annontation 是Java5 开始引入 的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation 像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation包中。
简单来说:注解其实就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相对应的处理。
传统的方式,我们是通过配置文件 .xml来告诉类是如何运行的。
有了注解技术之后,我们就可以通过注解告诉类如何运行
例如:我们以前编写 Servlet 的时候,需要在web.xml文件配置具体的信息。我们使用了注解以后,可以直接在Servlet 源代码上,增加注解……Servlet就被配置到Tomcat上了,也就是说,注解可以给类、方法上注入信息。
明显地可以看出,这样是非常直观的,并且 Servlet规范是推崇这种配置方式的。
在java.lang包下存在着5个基本的Annotation,重点掌握钱三个。
1.@Override 重写注解
①如果我们使用IDE重写父类的方法,我们就可以看见它了。
②@Override是告诉编译器要检查该方法是实现父类的。可以帮我们避免一些低级的错误。
③比如,我们在实现 equals() 方法的时候,把 euqals() 打错了,那么编译器就会发现该方法并不是实现父类的,与注解 @Override 冲突,于是就会报错。
2.@Deprecated 过时注解
①该注解也非常常见,Java 在设计的时候,可能觉得某些方法设计得不好,为 了兼容以前的程序,是不能直接把它抛弃的,于是就设置它为过时。
②Date对象中的 toLocalString() 就被设置成过时了
③当我们在程序中调用它的时候,在 IDE 上会出现一条横杠,说明该方法是过时的。
@Deprecated
public String toLocaleString() {
DateFormat formatter = DateFormat.getDateTimeInstance();
return formatter.format(this);
}
3.@SuppressWarnings 抑制编译器警告注解
①该注解在我们写程序的时候并不是很常见,我们可以用它来让编译器不给予我们警告
②当我们在使用集合的时候,如果没有指定泛型,那么会提示安全检查的警告
③如果我们在类上添加了@SuppressWarnings这个注解,那么编译器就不会给予我们警告了
4.@SafeVarargs Java 7“堆污染”警告
什么是堆污染呢??当把一个不是泛型的集合赋值给一个带泛型的集合的时候,这种情况就很容易发生堆污染。
这个注解也是用来抑制编译器警告的注解,用的地方并不多。
5.@FunctionalInterface 用来指定该接口是函数式接口
用该注解显式指定该接口是一个函数式接口。
1.Annotation 型定义为@interface,所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口。
2.参数成员只能是用public 或默认(default)这两个访问修饰符修饰
3.参数成员只能使用基本数据类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class等数据类型,以及这一些类型的数据
4.要获取类方法和字段的注解信息,必须通过Java的反射技术来获取Annotation对象,因为你除此之外没有别的获取注解对象的方法
5.注解也可以没有定义成员,不过这样注解就没啥用了 PS:自定义注解需要使用到元注解
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 水果名称注解
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
通俗的讲,泛型就是操作类型的占位符,即:假设占位符为 T ,那么此次声明的数据结构操作的数据类型为类型。
假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串甚至其他任何类型的数据进行排序,该如何实现?答案是可以使用泛型。
使用Java泛型的概念,我们可以写一个泛型方法类对一个对象数组排序。然后,调用该泛型方法来对整数数组、浮点数数组、字符串数组等进行排序。
你可以写一个泛型方法,该方法在调用时可以接受不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
下面是定义泛型方法的规则:
public class GenericMethodTest
{
// 泛型方法 printArray
public static < E > void printArray( E[] inputArray )
{
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
public static void main( String args[] )
{
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "整型数组元素为:" );
printArray( intArray ); // 传递一个整型数组
System.out.println( "\n双精度型数组元素为:" );
printArray( doubleArray ); // 传递一个双精度型数组
System.out.println( "\n字符型数组元素为:" );
printArray( charArray ); // 传递一个字符型数组
}
}
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类称为参数化的类或参数化的类型。
public class Box {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box integerBox = new Box();
Box stringBox = new Box();
integerBox.add(new Integer(10));
stringBox.add(new String("菜鸟教程"));
System.out.printf("整型值为 :%d\n\n", integerBox.get());
System.out.printf("字符串为 :%s\n", stringBox.get());
}
}
1.类型通配符一般是使用 ? 代替具体的类型参数。例如 List> 在逻辑上是 List,List等所有List<具体类型实参>的父类。
2.类型通配符上限通过形如 List 类定义,如此定义就是通配符泛型值接受 Number 及其下层子类类型。
3.类型通配符下限通过形如 List super Number>来定义,表示类型只能接受Number及其三层父类类型,如Object类型的实例。
理解编码的关键,是要把字符的概念和字节的概念理解准确。这两个概念容易混淆,我们在此做一下区分:
类型 | 概念描述 | 举例 |
---|---|---|
字符 | 人们使用的记号,抽象意义上的一个符号 | ‘1’,‘中’,‘a’,’$’…… |
字节 | 计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间 | 0x01,0x45,0xFA…… |
ANSI字符串 | 在内存中,如果“字符是以ANSI编码形式存在的,一个字符可能使用一个字节或多个字节来表示,那么我们称这种字符串为ANSI字符串或者多字节字符串 | “中文123”(占7字节) |
UNICODE字符串 | 在内存中,如果“字符”是以在UNNNICODE中的序号存在的,那么我们称这种字符串为UNICODE字符串或者字节字符串 | L"中文123"(占10 字节) |
Java 面向对象的基本思想之一是封装细节并且公开接口。Java 语言采用访问控制修饰符来控制类及类的方法和变量的访问权限,从而向使用者暴露接口,但隐藏实现细节。访问控制分为四种级别:
修饰符 | 当前类 | 同包 | 子类 | 其他包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
Java 中字符串对象创建有两种形式,一种为字面量形式,如 String str=“adc”; ,另一种就是使用new这种标准的构造对象的方法,如 String str = new String(“abc”); ,这两种方式我们在代码编写时都经常使用,尤其是字面量的方式。然而这两种实现其实存在着一些性能和内存占用的差别。这一切都是源于JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池。
当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回,否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用。
public class Test {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
// 以上两个局部变量都存在了常量池中
System.out.println(s1 == s2); // true
// new出来的对象不会放到常量池中,内存地址是不同的
String s3 = new String();
String s4 = new String();
/**
* 字符串的比较不可以使用双等号,这样会比较内存地址
* 字符串比较应当用equals,可见String重写了equals
*/
System.out.println(s3 == s4); // false
System.out.println(s3.equals(s4)); // true
}
}
我们使用工具编写的字母加符号的代码,是我们能看懂的高级语言,计算机无法直接理解,计算机需要先对我们编写的代码翻译成计算机语言,才能执行我们编写的程序。
将高级语言翻译成计算机语言有编译,解释两种方式。两种方式只是翻译的时间不同。
编译型语言写在程序执行之前,需要借助一个程序,将高级语言编写的程序翻译成计算机能懂的机器语言,然后,这个机器语言就能直接执行了,也就是我们常见的(exe文件)。
解释型语言的程序不需要编译,节省了一道工序,不过解释型语言在运行的时候需要翻译,每个语句都是执行的时候才翻译,对比编译型语言,效率比较低。通俗来说,就是借助一个程序,且这个程序能试图理解编写的代码,然后按照编写的代码中的要求执行。
脚本语言也是一种解释性语言,又被称为扩建的语言,或者动态语言不需要编译,可以直接使用,由解释器来负责解释。
脚本语言一般都是以文本形式存在,类似于一种命令、
同时讨论编译型语言和解释型语言的时候,这么说:编译型语言相当于做了一桌子菜再吃,解释型语言就是吃火锅。解释型的语言执行效率低。需要一边煮一边吃。
抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
继承:继承是从已知类得到继承信息创造新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。
多态:多态性是指允许不同子类型的对象对同一消息作出不同的响应。
举一个简单的例子,在物流信息管理系统中,有两种用户:订购客户和买房客户,两个客户都可以登录系统,他们有同样的 Login 方法,但登陆之后他们会进入不同的页面,也就是在登录的时候会有不同的操作,两种客户都继承父类的 Login方法,但是对于不同的对象,拥有不同的操作。
如果是C语言来展示出面向对象的思想,C语言中是不是有个叫结构体的东西,这个里面有自己定义的变量,可以通过函数指针就可以实现对象
单一职责(Single Responsibility Principle 简称 SRP):一个类应该仅有一个引起它变化的原因。在面向对象中,如果只让一个类完成它该做的事,而不涉及与它无关的领域就是践行了高内聚的原则,这个类就只有单一职责。
里氏替换(Liskov Substitution Principle 简称 LSP):任何时候子类型能够替换掉它们的父类型。子类一定是增加父类的能力而不是减少父类的能力,因为子类比父类的能力更多,把能力多的对象当成能力少的对象来用当然没有任何问题。
依赖倒置(Dependence Inversion Principle 简称 DIP):要依赖于抽象,不要依赖于具体类。要做到依赖倒置,应该做到:①高层模块不应该依赖底层模块,二者都应该依赖于抽象;②抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
接口隔离(Interface Segregation Principle 简称 ISP):不应该强迫客户依赖于他们不用的方法 。接口要小而专,绝不能大而全。臃肿的接口是对接口的污染,既然接口表示能力,那么一个接口只应该描述一种能力,接口也应该是高度内聚的。
最少知识原则(Least Knowledge Principle 简称 LKP):只和你的朋友谈话。迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。
开闭原则(Open Closed Principle 简称 OCP):软件实体应当对扩展开放,对修改关闭。要做到开闭有两个要点:①抽象是关键,一个系统中如果没有抽象类或接口系统就没有扩展点;②封装可变性,将系统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一起,系统将变得复杂而换乱。
单一职责、开发封闭、合成聚和复用(最简单的例子就是String类)、接口隔离
可以将一个类的定义放在另一个类的定义内部,这就是内部类。
在Java中内部类主要分为成员内部类、局部内部类、匿名内部类、静态内部类
成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
public class OuterClass {
private String str;
public void outerDisplay(){
System.out.println("outerClass...");
}
public class InnerClass{
public void innerDisplay(){
str = "chenssy..."; //使用外围内的属性
System.out.println(str);
outerDisplay(); //使用外围内的方法
}
}
// 推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时
public InnerClass getInnerClass(){
return new InnerClass();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.getInnerClass();
inner.innerDisplay();
}
}
--------------------
chenssy...
outerClass...
在成员内部类中要注意两点:
有这样一种内部类,它是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。
//定义在方法里:
public class Parcel5 {
public Destionation destionation(String str){
class PDestionation implements Destionation{
private String label;
private PDestionation(String whereTo){
label = whereTo;
}
public String readLabel(){
return label;
}
}
return new PDestionation(str);
}
public static void main(String[] args) {
Parcel5 parcel5 = new Parcel5();
Destionation d = parcel5.destionation("chenssy");
}
}
//定义在作用域内:
public class Parcel6 {
private void internalTracking(boolean b){
if(b){
class TrackingSlip{
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip(){
return id;
}
}
TrackingSlip ts = new TrackingSlip("chenssy");
String string = ts.getSlip();
}
}
public void track(){
internalTracking(true);
}
public static void main(String[] args) {
Parcel6 parcel6 = new Parcel6();
parcel6.track();
}
}
匿名内部类也就是没有名字的内部类。正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写。但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
实例1:不使用匿名内部类来实现抽象方法
abstract class Person {
public abstract void eat();
}
class Child extends Person {
public void eat() {
System.out.println("eat something");
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Child();
p.eat();
}
}
运行结果:eat something
可以看到,我们用 Child 继承了 Person 类,然后实现了 Child 的一个实例,将其向上转型为 Person 类的引用
但是,如果此处的 Child 类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?
这个时候就引入了匿名内部类
实例2:匿名内部类的基本实现
abstract class Person {
public abstract void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
运行结果:eat something
可以看到,我们直接将抽象类 Person 中的方法在大括号中实现了,这样便可以省略一个类的书写,并且,匿名内部类还能用于接口上。
实例3:在接口上使用匿名内部类
interface Person {
public void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
运行结果:eat something
由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现
最常用的情况就是在多线程的实现上,因为要实现多线程必须继承 Thread 类或是继承 Runnable 接口
实例4:Thread类的匿名内部类实现
public class Demo {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
t.start();
}
}
运行结果:1 2 3 4 5
实例5:Runnable接口的匿名内部类实现
public class Demo {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
Thread t = new Thread(r);
t.start();
}
}
运行结果:1 2 3 4 5
关键字 static 中提到 static 可以修饰成员变量、方法、代码块,其他它还可以修饰内部类,使用 static 修饰的内部类我们称之为静态内部类,不过我们更喜欢称之为嵌套内部类。静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。
public class OuterClass {
private String sex;
public static String name = "chenssy";
// 静态内部类
static class InnerClass1{
// 在静态内部类中可以存在静态成员
public static String _name1 = "chenssy_static";
public void display(){
// 静态内部类只能访问外围类的静态成员变量和方法
// 不能访问外围类的非静态成员变量和方法
System.out.println("OutClass name :" + name);
}
}
// 非静态内部类
class InnerClass2{
// 非静态内部类中不能存在静态成员
public String _name2 = "chenssy_inner";
// 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的
public void display(){
System.out.println("OuterClass name:" + name);
}
}
// 外围类方法
public void display(){
// 外围类访问静态内部类:内部类
System.out.println(InnerClass1._name1);
// 静态内部类 可以直接创建实例不需要依赖于外围类
new InnerClass1().display();
// 非静态内部的创建需要依赖于外围类
OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
// 方位非静态内部类的成员需要使用非静态内部类的实例
System.out.println(inner2._name2);
inner2.display();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.display();
}
}
----------------
Output:
chenssy_static
OutClass name :chenssy
chenssy_inner
OuterClass name:chenssy
public class Computer{
pblic Conmputer(){
CPU cpu=new CPU();
RAM ram=new RAM();
Disk disk = new Disk();
}
}
class CPU{ }
class RAM{ }
class Disk{ }
public class PlaneDelegation{
private PlaneControl planeControl; //private外部不可访问
// 飞行员权限代理类,普通飞行员不可以开火
PlaneDelegation(){
planeControl = new PlaneControl();
}
public void speed(){
planeControl.speed();
}
public void left(){
planeControl.left();
}
public void right(){
planeControl.right();
}
}
final class PlaneControl {// final表示不可继承,控制器都能继承那还得了
protected void speed() {}
protected void fire() {}
protected void left() {}
protected void right() {}
}
构造函数是函数的一种特殊形式。特殊在哪里?构造函数中不需要定义返回类型(void是无须返回值的意思,请注意区分两种),且构造函数的名称与所在的类名完全一致,其余的与函数的特性相同,可以带有参数列表,可以存在函数的重载现象。
一般用来初始化一些成员变量,当要生成一个类的对象(实例)的时候就会调用类的构造函数。如果不显示声明类的构造方法,会自动生成一个默认的不带参数的空的构造函数。
public class Demo{
private int num=0;
//无参构造函数
Demo()
{
System.out.println("constractor_run");
}
//有参构造函数
Demo(int num)
{
System.out.println("constractor_args_run");
}
//普通成员函数
public void demoFunction()
{
System.out.println("function_run");
}
}
这这里要说明一点,如果在类中我们不声明构造函数,JVM会帮我们默认生成一个空参数的构造函数;如果在类中我们声明了带参数列表的构造函数,JVM就不会帮我们默认生成一个空参数的构造函数,我们想要使用空参数的构造函数就必须自己去显示的声明一个空参的构造函数。
通过开头的介绍,构造函数的轮廓已经渐渐清晰,那么为什么会有构造函数呢?构造函数有什么作用?构造函数是面向对象编程思想需求的,它的作用有一下两个:
父类引用能指向子类对象,子类引用不能指向父类对象;
父类引用指向子类对象,例如:
Father f1=new Son();
把指向子类对象的父类引用赋给子类引用,需要强制转换,例如:
Father f1 = new Son();
Son s1 = (Son)f1;
但是运行出错的情况:
Father f2=new Father();
Son s2=(Son)f2; //编译无错但运行会出现错误
在不确定父类引用是否指向子类对象时,可以用instanceof 来判断:
if(f3 instanceof Son){
Son s3 = (Son) f3;
}
1. 数据
声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。
对于基本类型,final 使数值不变;
对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
final int x = 1;
// x = 2; // cannot assign value to final variable 'x'
final A y = new A();
y.a = 1;
2. 方法
声明方法不能被子类覆盖。
private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是覆盖基类方法,而是在子类中定义了一个新的方法。
3. 类
声明类不允许被继承。
1. 静态变量
静态变量在内存中只存在一份,只在类初始化时赋值一次。
public class A {
private int x; // 实例变量
public static int y; // 静态变量
}
注意:不能再成员函数内部定义static变量。
2. 静态方法
静态方法在类加载的时候就存在了,它不依赖于任何实例,所以静态方法必须有实现,也就是说它不能是抽象方法(abstract)。
静态语句块在类初始化时运行一次。
内部类的一种,静态内部类不依赖外部类,且不能访问外部类的非静态的变量和方法。
import static com.xxx.ClassName.*
在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。
静态变量的赋值和静态语句块的运行优先于实例变量的赋值和普通语句块的运行,静态变量的赋值和静态语句块的运行哪个先执行取决于它们在代码中的顺序。
public static String staticField = "静态变量";
static {
System.out.println("静态语句块");
}
public String field = "实例变量";
{
System.out.println("普通语句块");
}
最后才运行构造函数
public InitialOrderTest() {
System.out.println("构造函数");
}
存在继承的情况下,初始化顺序为:
父类(静态变量、静态语句块)
子类(静态变量、静态语句块)
父类(实例变量、普通语句块)
父类(构造函数)
子类(实例变量、普通语句块)
子类(构造函数)
跳出当前循环;但是如果是嵌套循环,则只能跳出当前的这一层循环,只有逐层 break 才能跳出所有循环。
for (int i=0; i < 10; i++){
// 在执行i==6是强制终止循环,i==6不会被执行
if(i == 6 ){
break;
}
System.out.println(i);
}
终止当前循环,但是不跳出循环(在循环中continue 后面的语句是不会执行了),请继续往下根据循环条件执行循环。
for (int i = 0; i < 10; i++) {
// i==6不会被执行,而是被中断了
if(i==6) {
continue;
}
System.out.println(i);
}
特别注意:返回值为void 的方法,从某个判断中跳出,必须用return。
final 用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖和类不可被继承。
是保证代码一定要被执行的一种机制.常用来关闭连接资源或者解锁等.
是Object的一个方法,它的目的是保证对象在被垃圾收集前完成特定资源的回收.1.9后已经过时
类(class)、接口(interface)、数组(array)
// jdk 1.5
public class TestDemo {
public static void main(String[] args) {
Integer m =10;
int i = m;
}
}
上面的代码在 jdk1.5 只后不会报错,1.5出现了自动装、拆箱。1.5之前会报错。