欢迎来到我的博客。本部分主要是记录我学习Java笔记以及在学习过程中遇到的一些问题。
1.JDK与JRE的安装
- 安装8版本的JDK,下载好安装包之后一路傻瓜式安装即可(记得更改安装路径),在安装JDK的同时也会提醒安装JRE,按提示安装即可
2.搭建环境变量
- 在用户变量中,点击创建,变量名命名为JAVA_HOME(必须为这个名字),变量值则为你安装java的路径,eg:’D:\set app\java’
- 在用户变量中找到变量为path(如果没有就创建),然后单击之后编辑,选择新建,输入
%JAVA_HOME%\bin
即可,然后点击确认就配置完成了- 自检:打开cmd,输入
java -version
检查版本号,之后输入java
与javac
,两者都正常运行即表示环境变量配置成功- 注:当系统不能正确的识别当前用户,则在系统变量里进行上述的步骤即可
3.软件推荐
- 软件推荐使用Eclipse(主要是免费),点击安装时选择安装 Eclipse IDE for Java Developers 即可(软件安装较慢,耐心等待)
4.基础知识学习
输出语句:
System.out.println();
键盘中获取不同数据类型的变量
- 第一步:导包
import java.util.Scanner;
- 第二步:创建Scanner对象
Scanner scan = new Scanner(System.in);
- 第三部:调用Scanner类相关方法
int score = scan.nextInt()
Java基本数据类型:在定义long、float、double类型的时候,在数据的尾部必须要加上L、F、D(大小写均可),因为其相对的默认值分别为0F、0.0F、0.0D。对于byte、short、int等类型直接定义即可
一维数组
- 声明:声明一个int类型的一维数组:
int[] arr;
与int arr[];
均可- 静态初始化(直接对元素进行赋值):
arr = new int[]{1,2,3,4};
- 动态初始化(初始化与元素赋值分开):
String names[] = new String[4];
(并没有直接给数组元素进行赋值)- 数组的复制:
- 错误的复制:
1
2
3 int arr1[],arr2[];
arr1[] = new int[]{1,2,3};
arr2=arr1;//这样复制,仅仅是将arr1的地址复制给arr2- 正确的复制
1
2
3
4
5 int arr1[],arr2[];
arr1[] = new int[]{1,2,3};
for(int i = 0;i<arr1.length;i++){
arr2[i] = arr1[i];//将数组arr1中的所有的值赋值给arr2
}二维数组
- 声明:声明一个int类型的二维数组:
int[][] arr;
与int arr[][];
均可- 静态初始化(直接对元素进行赋值):
int arr[][] = new int[][]{{1,2,3},{4,5},{6,7,8}};
- 动态初始化(初始化与元素赋值分开):
String arr[][] = new String[3][2];
(并没有直接给数组元素进行赋值)- 动态初始化的另一种方式:
String arr[][] = new String[3][];
,之后给一维数组进行动态赋值:arr[1] = new String[2];
(代表二维数组中第二行有两个元素)
5.面向对象(Java学习的重点)
面向对象程序设计的重点是类的设计,设计类,技术设计类的成员
完整的类由属性、构造器、方法构成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 public class PersonTest{
public static void main(String[] args){
Person p1 = new Person()//其实他就是调用Person里的构造器,平时我们没有进行定义构造器,系统会默认提供一个空参的构造器
}
}
class Person{
//属性
int age;
String name;
// 一般会默认的提供一个空参的构造器
public Person(){
}
//构造器
public Person(){
System.out.prinln("这是构造器");
}
//方法
public void eat(){
System.out.prinln("吃饭");
}
}构造器/构造方法/constructor
- eg:Person p1 = new Person(),其中Person()就称为构造器,new Person()为创建构造器
- 作用:创建对象、初始化对象的属性
- 如果没有显示的定义类的构造器,系统默认提供一个空参的构造器
- 一个类中定义多个构造器,彼此构成重载
- 一旦我们显示的定义了类的构造器之后,系统就不再提供默认的空参构造器
类和对象是面向对象的核心概念
- 类是对一类事物的描述,是抽象的、概念上的定义
- 对象是实际存在的该类事物的每个个体,因而也称为实例
数组与对象的结合使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 public class studentest {
public static void main(String[] args){
student s[] = new student[20]; //创建一个student类型的数组
for(int i=0;i<20;i++){
s[i]= new student(); //给每一个数组元素进行赋值
s[i].number=i+1;
s[i].state=(int)(Math.random()*(6-1+1)+1);
s[i].store = (int)(Math.random()*100+1);
}
for(int i =0;i<20;i++){
System.out.println(s[i].state);
}
}
}
class student{
int number;
int state;
int store;
}值传递机制(不是引用传递)
- 如果参数是基本数据类型,此时实参赋给形参的就是实参真实存储的数据值
- 如果参数是引用数据类型,此时实参赋给形参的就是实参存储数据的地址值
权限修饰符
- Java规定的四种权限修饰符(从小到大):private、缺省、protected、public
- 权限修饰符可以用来修饰类以及类的内部结构:属性、方法、构造器、内部类
- 对于 class 名称 的权限修饰只能用public和default(缺省)
- 权限修饰符访问权限:
修饰符 类内部 同一个包 不同包的子类(extends) 同一个工程 private Yes (缺省) Yes Yes protected Yes Yes Yes public Yes Yes Yes Yes 权限修饰符private的使用
- 当在数据类型前加上private时,代表仅仅只能在改方法/函数中调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 public class Test{
public static void main(String[] args){
Animal a = new Animal();
a.name = "鲸落";
a.legs = 2;
//当在int ages 前加入private后,在Test中无法调用ages,调用则会报错
a.ages = 18;
//当我们想使用ages是,只能通过调用setLegs函数来实现
}
}
class Animal{
String name;
private int ages;
int legs;
//对属性的设置:set方法
public void setLegs(int age){
if( age > 0 && age % 2 == 0){
ages = age;
}
else{
ages = 0;
//或者选择抛出一个异常来告诉使用者
}
}
//对属性的获取:get方法
public int getAge(){
return Age;
}
}关键字this的使用
- this可以用来修饰、调用:属性、方法、构造器
- this修饰属性和方法,this可以理解为当前对象或当前正在创建的对象
- this调用构造器:
- 我们在类的构造器中,可以显示的使用“this(形参列表)”的方式,调用本类中指定的其他构造器
- 构造器中不能通过“this(形参列表)”的方式来调用自己
- 如果我们有n个构造器,则最多有n-1个构造器中使用了“this(形参列表)”,因为构造器之间不能形参“环”
- 规定:使用“this(形参列表)”时,必须在声明在当前构造器的首行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 public class PersonTest{
public static void main(String[] args){
Person p1 = new Person("鲸落",18);
}
}
class Person{
private int age;
private String name;
public Person(){
this.eat();
System.out.println("Person初始化的时候执行n行代码");
}
public Person(String name){
this(); //调用public Person()构造器
this.name = name;
}
public Person(int age){
this(); //调用public Person()
this.age = age;
}
public Person(String name,int age){
this(age); //调用public Person(int age)
this.name = name;
this.age = age;
}
}关键字super的使用
- super可以理解为父类的,通过使用“supper.属性”或“super.方法”的方式,来调用父类中声明的属性或方法
- 当子类与父类定义了同名的属性或父类中的方法在子类中被重写后,要想在子类中调用父类声明的属性,使用“super.属性”的方式,表明调用父类中声明的属性
- this与super的区别:当你使用“this.属性”的时候,现在子类中华查找该属性,没有则前往父类去查找,使用“super.属性”则直接在父类中查找
- super调用构造器与 this 相同,调用方式为“super(形参列表)”并且在使用的时候,与this一样,必须声明在子类构造器的首行,因此我们在调用构造器的时候,针对与“this(形参列表)”与“super(形参列表)”只能二选一,不能同时出现
- 在构造器的首行,没有显示声明“this(形参列表)”或“super(形参列表)”,则默认调用父类中空参的构造器,即super(),所以我们在子类定义构造器的时候,父类也一定要定义一个空参的构造器,否则会报错
关键字package的使用:使用package声明类或接口所属的包,声明在源文件的首行,eg:package cn.xiaojunnan.test
关键字import(导入)的使用:在源文件中显示的使用import结构导入指定包下的类、接口
关键字static的使用
- static可以用来修饰:属性、方法、代码块、内部类,注意static不能用来修饰构造器
- static修饰属性:静态变量(类变量)
- 我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的
- 随着类的加载而加载,可以通过“类.静态变量”的方式进行调用
- 静态变量的加载要早于对象的创建,因为类的加载要早于对象的创建
- 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中
- 静态属性举例:System.out、Math.PI
- static修饰方法:静态方法
- 随着类的加载而加载,可以通过“类.静态方法”的方式进行调用
- 静态方法中,只能用静态的方法或属性,而非静态方法中,既可以调用静态的方法或属性,也可以调用非静态的方法或属性
- static注意点:
- 在静态的方法中,不能使用this、super关键字来调用属性与方法
- 关于静态属性与静态方法的使用,可以从生命周期的角度去理解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 public class StaticTest{
public static void main(String[] args){
chinese.nation = "中国"; //static静态变量,随着类的加载而加载,早于对象的创建,所以可以直接调用不报错
chinese c1 = new chinese();
c1.name = "鲸落";
c1.age = 18;
c1.nation = "China";
chinese c2 = new chinese();
c2.name = "君的鲸落";
c2.age = 20;
System.out.println(c2.nation);//China 我们没有给c2.nation赋值,输出为China,因为给nation设置了static
c2.nation = "CHINA";
System.out.println(c1.nation);//CHINA 在执行c2.nation将c1.nation中的nation值改变
}
}
class chinese{
String name;
int age;
static String nation;
}关键字final
- final可以用来修饰的结构:类、方法、变量
- 当将一个类被final修饰时(
final class one
),该类则将不能被继承(class two extends one
),继承即会报错- final修饰一个方法时,表明此方法不能被重写,eg:String类、System类
- final修饰变量,此时变量变成一个常量了,不能再继续改变
- final修饰属性:可以考虑赋值的位置有:显示初始化、代码块、构造器中初始化
- final修饰局部变量:final修饰形参的时候,表明形参是一个常量。我们调用此方法时,给常量形参赋一个值,一旦赋值,只能在方法体中使用形参,不能进行重新赋值
继承性(extends)
- 格式:class A extends B{}
- A:子类,B:父类
- 一旦子类A继承父类B之后,子类A就获取了父类B中声明的属性和方法
- 特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只有由于封装性的影响,使得子类不能直接调用父类的结构而已,可以通过get与set方法进行调用设置
- 一个子类只能有一个父类,但一个父类可以有多个子类(Java中的单继承性)
1
2
3
4
5
6
7
8
9
10 //student.java文件与person.java文件需要在同一包下
//student.java文件
public class student{
int age;
String name;
}
//person.java文件,加上extends student即可继承student里面的age与name,在person中就无需定义了
public class person extends student{
String info;
}接口(interface)
- 接口使用interface来定义
- Java中,接口(interface)与类(class)是并列的两个结构
- 如何定义接口:定义接口中的成员
- JDK7及以前:只能定义全局常量和抽象方法
- 全局常量:public static final
- 抽象方法:public abstract
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public class interfaceTest{
public static void main(String[] args){
//在 class Plane implements Flyable()后,可以进行实例化
Plane p = new Plane();
p.fly();
p.stop();
}
}
interface Flyable{
//全局常量
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1; //在interface中定义,public static final 可以省略
//抽象方法
public abstract void fly();
void stop(); //与全局常量的定义同理
}
interface Attackable(){
void attack();
}
class Plane implements Flyable(){
public void fly(){
System.out.println("fly");
}
public void stop(){
System.out.println("stop");
}
}
//实现多个接口,当然内部需要将所以接口的抽象方法重写
class Bullet implements Attackable,Flyable{
public void attack(){
System.out.println("attack");
}
public void fly(){
System.out.println("fly");
}
public void stop(){
System.out.println("stop");
}
}
//接口与接口之间的多继承:
interface AA{
}
interface BB{
}
interface CC intends AA,BB{
}- JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法与默认方法
- 全局常量:public static final int one;
- 抽象方法:public abstract void method();
- 静态方法:
- 写法:public static void method(){System.out.println(“静态方法”)};
- 接口中定义的静态方法,只能通过接口来调用
- 默认方法:
- 写法:public default void method(){System.out.println(“默认方法”)};
- 接口中定义的默认方法,可以通过实现类的对象来调用
- 如果实现类中重写了接口中默认方法,调用时调的是重写的方法
- 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么在子类没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法 ————> 类优先原则
- 如果实现类实现了多个接口,而在这多个接口中定义了同名同参数的默认方法,那么在实现类没用重写此方法的情况下,报错 ————> 接口冲突
- 在子类(实现类)的方法中调用父类:XX.super.method()(XX为被实现类),super.method()为调用父类的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 public interface interfaceTest{
//静态方法
public static void method1(){
System.out.println("静态方法");
}
//默认方法
public default void method2(){
System.out.println("默认方法");
}
}
public class SubclassTest{
public static void main(String[] args){
Subclass sub = new Subclass();
sub.method1(); //无法被调用,接口中定义的静态方法,只能通过接口来调用
interfaceTest.method1(); //静态方法
sub.method2(); //默认方法Subclass 调重写的方法
}
}
public class Subclass implements interfaceTest{
public void method2(){
System.out.println("默认方法Subclass");
}
}- 接口中不能定义构造器,也意味着接口不可实例化
- Java开发中,接口通过让类去实现(implements)的方式来使用
- 如果接口中有抽象方法,则实现类中要么覆盖该方法,要么定义为抽象类(abstract的使用中提到)
- Java类可以实现多个接口
- 有继承,有实现:class AA extends BB implements CC,DD,EE;
- 接口与类之间的关系叫实现,类与类之间的关系叫继承,接口与接口之间也是继承,且是多继承
方法的重写
- 子类继承父类之后,可以对父类中的同名同参数进行覆盖操作,子类重写的方法的方法名与形参列表与父类被重写的方法的方法名与形参列表相同
- 子类重写的方法的权限修饰符大于父类被重写的方法的权限修饰符
- 特殊情况:子类不能重写父类中声明为private权限的方法
- 返回值类型:
- 父类被重写的返回值类型是void,则子类重写的方法返回值类型只能是void
- 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类(eg:父类是Object类型,子类则可返回String类型)
- 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
- 子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(其实加上static就不属于重写)
- 可变个数形参:
public void test(String ... strs)
,定义该方法与多个实参相匹配,当定义多个不同类型的形参时,可变个数形参需写在最后,且一个方法只能存在一个可变个数形参,eg:public void test(int one,String ... strs)
- 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型,当父类的方法中没有抛异常时,子类也一定不能抛
多态性
- 对象的多态性:父类的引用指向子类的对象(左侧为父类的声明,右侧为子类的声明)
- 多态性的前提:
- 类的继承性
- 方法的重写
- 对象的多态性,只适用于方法,不适用于属性
- 多态性的使用:编译看左边,调用看右边
- 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类的方法转移到子类中
- 多态性不能调用子类所特有的方法
- 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 //Person.java中:
public class Person{
int age=100;
String name;
public void eat(){
System.out.println("Person中的吃饭");
}
public void study(){
System.out.println("Person中的学习");
}
}
//Man.java中:
public class Man extends Person{
int age = 200;
public void eat(){
System.out.println("Man中的吃饭");
}
public void walk(){
System.out.println("man走路");
}
}
//Woman.java中:
public class Woman extends Person{
}
personTest中:public class personTest{
public static viod main(String[] args){
//正常调用:
Person p1 = new Person();
//多态性:
Person p2 = new Man();
Person p3 = new Woman();
//调用方法:p2调用只能调用父类中的方法和在子类中重写的方法,调用的时候编译的是父类中的方法,当子类重写之后会调用子类的方法,没有重写则调用父类的方法
p2.eat()//结果为 Man中的吃饭,子类有重写的方法就调用子类,没有就调用父类
p2.study()//结果为 Person中的学习
p2.age;//结果为 100 因为对象的多态性只适用于方法,不适用于属性
p2.walk()//报错,无法调用,只能调用Person中声明的方法,在子类中重写
}
}- 多态性的使用举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public class AnimalTest{
public Static void main(String[] args){
AnimalTest test = new AnimalTest();
func(new Dog()); //虚拟方法调用,声明的是animal,调用的dog
}
public void func(Animal animal){ //Aniaml animal = new Dog();即为多态性
animal.eat();
animal.shout();
}
}
class Animal{
public void eat(){
System.out.println("动物吃");
}
public void shout(){
System.out.println("动物叫");
}
}
class Dog{
public void eat(){
System.out.println("狗吃骨头");
}
public void shout(){
System.out.println("汪汪汪");
}
}
class Cat{
public void eat(){
System.out.println("猫吃鱼");
}
public void shout(){
System.out.println("喵喵喵");
}
}- 有了对象的多态性之后,内存中实际上是加载了子类中特有的属性和方法,但是由于变量声明为父类类型,导致编译时只能调用父类中声明的属性和方法,子类中特有的属性和方法不能调用。那么如何才能调用子类特有的属性和方法呢,答案是——向下转型:使用强制类型转符
- 向下转型:使用强制类型转换符:
Man m1 = (Man)p2
(不推荐,可能会出现ClassCastException异常)- instanceof关键字的使用
- 为了避免在向下转型前出现异常,进行一个判断,使用instanceof
- a instanceof A:判断对象a是否是类A的实例,如果是,返回true,如果不是,返回false
1
2
3
4
5
6
7
8
9
10
11
12
13 Man m1 = (Man)p2 //p2原来是Person类型,强制转换成Man类型在赋给m1
if(p2 instanceof Woman){
Woman w1 = (Woman)p2;
w1.goShopping();
System.out.println("女人逛街");
}
if(p2 instanceof Man){
Man m2 = (Man)p2
w1.earnMoney();
System.out.println("男人赚钱");
}
//执行结果:男人赚钱 //第一个if语句没有进入,因为在第一行将p2强制转为Man了- 常见错误:
- 问题一:编译时通过,运行时不通过:new的是Man,不能转换成Woman
1
2 Person p3 = new Man();
Woman m3 = (Woman)p3;- 问题二:编译时通过,运行时不通过:new的是Person,不能强转为子类
1
2 Person p4 = new Person();
Man m4 = (Man)p4Object类
- Object类是所有Java类的根父类,如果在类的声明中没有使用extands关键字指明其父类,则默认父类为java.lang.Object类
- equals()方法
- toString()方法
- 当我们输出一个对象的引用时,实际上调用的就是当前对象的toString()方法
1
2
3
4
5
6
7
8 Customer cust = new Customer();
System.out.println(cust.toString());
System.out.println(cust); //两者打印值相同,返回地址值
//Object()类中的toString()方法
public String toString(){
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}- 像String、Date、File、包装类等都重写了Object类中的toString()方法,使得在调用的时候返回实体内容
包装类(Wrapper)
- java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征,就可以调用类的方法,Java才是真正的面向对象
基本数据类型 byte short int long float double boolean char 包装类 Byte Short Integer Long Float Double Boolean Character - 其中,除了Boolean与Character,其他的父类都为Number
- 基本数据类型 —> 包装类:调用包装类的构造器
1
2
3
4
5
6
7
8
9
10
11 public class WrapperTest{
public void test1(){
int num = 10;
Integer num1 = new Integer(num);
System.out.println(num1.toString());//10
Double num2 = new Double(2.0);
System.out.println(num2);//2.0
}
}- 包装类 —> 基本数据类型:调用包装类的xxxValue()
1
2
3
4
5
6
7
8
9
10
11 public class WrapperTest{
public void test2(){
Integer num1 = new Integer(12);
int int1 = num1.intValue();
System.out.println(int1++);//13
Float f1 = new Float(12.3f);
float f2 = f1.floatValue();
System.out.println(f2+1);//13.3
}
}- JDK 5.0新特性:自动装箱与自动拆箱
1
2
3
4
5
6
7
8
9
10
11
12
13
14 public class WrapperTest{
public void test3(){
//自动装箱:基本数据类型 ---> 包装类
int num = 10;
Integer num1 = num;//不需要new一个构造器了
boolean b1 = true;
Boolean b2 = b1;
//自动拆箱:包装类 ---> 基本数据类型
int num2 = num1;
}
}- 基本数据类型、包装类 —> String()类型 :调用String重载的valueOf()
1
2
3
4
5
6
7
8
9
10
11
12
13 public class WrapperTest{
public void test4(){
int num = 10;
//方法一:连接运算
String num1 = num + "";
//方法二:调用String重载的valueOf()
float f1 = 12.3f;
String str = String.valueOf(f1);
System.out.println(str);//12.3
}
}- String()类型—> 基本数据类型、包装类 :调用包装类的parseXXX()方法
1
2
3
4
5
6
7
8
9
10
11
12 public class WrapperTest{
public void test5(){
String str1 = "123";
int num4 = Integer.parseInt(str1);
System.out.println(num4+1);//124
String str2 = "true";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(b1);//true
}
}- 思考题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 public class WrapperTest{
public void test6(){
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//false
Integer m = 1;
Integer n = 1;
System.out. println(m == n);//true
//因为Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],
//保存了从-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在
//-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的:提高效率
Integer x = 128;//相当于new了一个Integer对象
Integer y = 128;//相当于new了一个Integer对象
System.out. println(x == y);//false
}
}类的内部成员之四:代码块
- 类的成员:方法、属性和构造器
- 作用:用来初始化当前的类与对象
- 一个类中定义了多个代码块,按顺序执行
- 代码块如果有修饰的话只能用static修饰
- 静态代码块
- 随着类的加载而执行,直接执行,且仅执行一次
- 作用:初始化类的信息
- 静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
- 非静态代码块
- 随着对象的创建而执行,直接执行,且每创建一次对象都执行一次
- 作用:可以在创建对象的时候,对对象的属性等进行初始化
- 非静态代码块内可以调用静态的属性、静态的方法,或静态的属性、静态的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 public class BlockTest{
public static void main(String[] args){
String desc = Person.desc; ——>执行静态代码块
Person p1 = new Person(); ——>执行非静态代码块
}
}
class Person{
//属性:
int age;
String name;
static String desc = "我是一个人";
//构造器:
public Person(){
}
//方法:
public void eat(){
System.out.println("吃饭");
}
//静态代码块:
static{
System.out.println("static代码块");
}
//非静态代码块
{
System.out.println("代码块");
}
}类的内部成员之五:内部类
- Java允许将一个类A声明在一个类B中,则类A就是内部类,类B称为外部类
- 内部类的分类:
- 成员内部类(静态、非静态)
- 作为外部类的成员
- 调用外部类的成员
- 被static修饰
- 四种权限修饰符修饰
- 作为一个类
- 定义方法、属性、构造器
- final修饰,表示此类不能被继承
- abstract修饰,表示不能被实例化
- 局部内部类(方法类、代码块类、构造器类)
- 在局部内部类的方法中(如show),如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如: num)的话,要求此局部变量声明为final
- 实例化内部类成员
- 调用外面类结构
- 方法的形参:eg:name;
- 内部类的属性:this.name;
- 外部类的属性:Person.this.name;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public class InnerClassTest{
public static void main(String[] args){
//创建静态的成员内部类
Person.Eating eat = new Person.Eating();
eat.eat();
//创建非静态的成员内部类
Person p = new Person();
Person.Listening listen = p.new Listening();
listen.listen();
}
}
class Person{
//直接定义:非静态成员内部类
class Listening{
public void listen(){
System.out.println("listen");
}
}
//直接定义:静态成员内部类
static class Eating{
public void eat(){
System.out.println("eat");
}
}
public Person(){
//定义在构造器中的局部内部类
class AA{
}
}
public void method(){
//定义在方法中的局部内部类
class AA{
}
}
{
//定义在代码块中的局部内部类
class AA{
}
}
}抽象类与抽象方法——abstract关键字的使用
- abstract修饰的结构:类、方法
- abstract修饰类:抽象类
- 此类不能实例化(即不能造对象)
- 抽象类中还是需要提供构造器,因为子类对象实例化中需使用
- 一般开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
- abstract修饰方法:抽象方法
- 抽象的方法只有方法的声明,没有方法体。
- 包含抽象方法的类一定是一个抽象类(保证了抽象方法不被调用),反之抽象类中可以没有抽象方法
- 父类中存在抽象方法,为保证子类中也不调用,要么选择给子类也设置成abstract抽象类,要么在子类中对父类的抽象方法进行重写
- abstract不能用来修饰属性、构造器等结构,也不能用来修饰private的方法、static的方法、final的方法、final的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 public class abstractTest{
public static void main(String[] args){
//未加上static:
Person p1 = new Person();
p1.eat(); //人吃饭
//class Person ——> static class Person
Person p2 = new Person() //报错,一旦Person类抽象了,就不可实例化了
}
}
abstract class Person{
String name;
int age;
public Person(){
}
public Person(String name,int age){
this.name = name;
int.age = age;
}
public void eat(){
System.out.println("人吃饭");
}
public void walk(){
System.out.printlm("人走路");
}
//抽象方法:
public abstract void eat();
}
6.异常处理
java.lang.Exception(可以进行异常的处理)
- 编译时异常:checked
- IOException
- FileNotFoundException
- ClassNotFoundException
- 运行时异常:unchecked
- NullPointerExceptiom
- ArrayIndexOutOfBoundException
- ClassCastException
- NumberFormatException
- InputMismatchException
- ArithmeticException
异常处理机制
- 异常的处理:抓抛模型
- 过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出,且一旦对象抛出之后,其后的代码将不会执行
- 过程二:“抓”:抓住抛出的对象,可以理解为异常处理的方式,即try-catch-finally与throws
- try-catch-finally
- catch中的异常类型如果没有子父类的关系,声明的顺序没有关系,但如果满足子父类的关系,则要求子类一定要声明在父类的关系
- 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配,一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没有写finally的情况)。继续执行其后的代码
- 常见的异常对象的处理方式:getMessage() printStackTrace()
- 在try结构中声明的变量,再出了try结构以后,就不能被调用
- finally是可选的,在finally中声明的所有代码一定会被执行,即使catch中又出现了异常或try中有return语句,catch中有return语句等。
- 像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 //格式:
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}finally{
//一定执行的软件
}
//eg:
public void test1(){
String str = "123";
str = "abc";
try{
int num = integer.parseInt(str);
}catch(NumberFormatException e){
System.out.println("数值转换出现异常,请稍等。。。");
System.out.println(e.getMessgae());
System.out.println(e.printStackTrace());
}
}- throws + 异常类型
- 即将当前的异常向上抛,哪里调用它,就在哪里使用try-catch进行处理
- “throws + 异常类型”写在方法的声明处,指明调用此方法可能出现的异常,将其抛出
- “throws + 异常类型”抛出的方法中,当调用出现异常时,异常代码后续代码将不会被执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 public class throwsTest(){
public static void main(String[] args){ //main中对异常进行处理
try{
method2();
}catch(IOException e){
System.out.println(e.printStackTrace());
}
}
public void method2() throws IOException{ //继续抛给调用它的函数main()
method1();
}
public void method1() throws FileNotFoundException,IOException{ //抛给调用它的函数method2()
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.println((char)data);
data = fis.read();
}
fis.close();
}
}- 开发中如何选择使用try-catch-finally还是使用throws
- 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着当子类重写的方法有异常的时候,必须要用try-catch-finally来处理
- 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式来处理,而执行的方法a可以考虑用try-catch-finally来处理
手动抛出异常
1
2
3
4
5
6
7
8
9
10
11
12 class Student{
private int id;
public void method(int id){
if(id > 0){
this.id = id;
}else{
//手动抛出异常
throw new RuntimeException( "您输入的数据非法!"); //运行时异常
throw new Exception("您输入的数据非法! "); //编译时异常,手动抛出编译时异常时,就需要对调用method()的方法进行处理
}
}
}** 异常类**
- 如何自定义异常类
- 第一步:继承于现有的异常结构:RuntimeException,Exception,eg:
class Test extend RuntimeException{}
- 第二步:提供全局常量:serialVersionUID,eg:
static final long serialVersionUID = -7034897190745766939L
- 第三部:继承重载的构造器,一般设置为:eg:
1
2
3
4
5
6 public Ecdef(){
super();
}
public Ecdef(String msg){
super(msg);
}
7.Java高级程序
- 多线程
- 多线程的创建步骤
- 继承于Thread类
- 创建一个继承于Thread类的子类
- 重写Thread类的run() —> 将此线程执行的操作声明在run()中
- 创建Thread类的子类的对象
- 通过此对象调用Thread类中的start() (start()方法的作用:启动当前线程与调用当前线程的run()方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 //1、例子:遍历100以内的所有的偶数
public class ThreadTest(){
//3、创建Thread类的子类的对象
MyThread t1 = new MyThread();
//4、通过此对象调用Thread类中的start()
t1.start();
//问题一:我们不能通过直接调用run()方法来启动线程,这样就是调用方法了,没有启动线程
t1.run()
//如下方法还是在mian线程(主线程)中执行
for(int i = 0; i<100;i++){
if(i%2 == 0){
System.out.println(i + "***main***");
}
}
}
//创建一个继承于Thread类的子类:
class MyThread extends Thread{
//2、重写Thread类的run() ---> 将此线程执行的操作声明在run()中
public void run(){
for(int i = 0; i<100;i++){
if(i%2 == 0){
System.out.println(i);
}
}
}
}- Thread()中常用的方法 (Thread.currentThread()是获取主线程)
- start():启动当前线程,调用当前线程的run()方法
- run():通常需要重写Thread类中的方法,将创建的线程要执行的操作声明在此方法中
- currentThread():静态的方法,返回执行当前代码的线程
- getName():获取当前线程的名字,eg:
Thread.currentThread().getName()
- setName():设置当前线程的名字,eg:
new Thread().setName("线程一")
8.区别解析
Java中单引号与双引号的区别
- 单引号表示字符,双引号表示字符串
- 单引号引出的数据类型一般是char类型,双引号引出的数据类型一般是String类型
- 单引号中只能放一个字母或数字或符号,双引号中可由多个字符构成。
- 在使用
+
的时候,单引号会将字符自动转为int类型参与,双引号则视为字符串对待
- 补充:查看数据类型:
- 1、定义方法
1
2
3
4
5 public TestAll{
public String getType(Object o){
return o.getClass().getName();
}
}- 2、调用方法
1
2
3
4
5
6
7
8
9 public class Test {
public static void main(String[] args){
int a = 1;
int b = 3;
TestAll t = new TestAll();
System.out.println(t.getType(a+','+b));//int
System.out.println(t.getType(a+","+b));//String
}
}‘==’和equals()的区别
- ‘==’:运算符
- 可以使用在基本数据类型变量和引用数据类型中
- 如果比较的是基本数据类型变量:比较两个变量保存的数值是否相等(不一定类型要相同)
1
2
3
4
5
6
7
8
9
10
11
12 int i = 10;
int j = 10;
double d = 10.0;
System.out.println(i == j); //true
System.out.println(i == d); //true
char c = 10;
System.out.println(i == c); //true
char a1 = 65;
char a2 = 'A';
System.out.println(a1 == a2); //true- 如果比较的是引用数据类型变量:比较两个对象的地址值是否相同
1
2
3 Customer cust1 = new Customer("Tom",21);
Customer cust2 = new Customer("Tom",21);
System.out.println(cust1 == cust2); //false- equals()
- 只能适用与引用数据类型
1
2
3
4
5
6
7 Customer cust1 = new Customer("Tom",21);
Customer cust2 = new Customer("Tom",21);
System.out.println(cust1.equals(cust2)); //false
String str1 = new String("abcd");
String str2 = new String("abcd");
System.out.println(str1.equals(str2)); //true 解释如下:- Object()类中equals()的定义如下,说明在Object()中定义的equals()和==的作用是相等的,比较两个对象的地址值是否相同,即两个引用是否指向同一地址
1
2
3 public boolean equals(Object obj){
return (this == obj);
}- 像String、Date、File、包装类等都重写了Object类中的equals()方法,重写之后比较的不是两个引用的地址是否相同,而是比较内容是否相同
throw与throws的区别
- throw 在方法体内使用,throws在方法声明上使用
- throw 后面接的是异常对象,只能接一个,throws 后面接的是异常类型,可以接多个,多个异常类型用逗号隔开
- throw 是在方法中出现不正确情况时,手动来抛出异常,结束方法的,执行了 throw 语句一定会出现异常。而 throws 是用来声明当前方法有可能会出现某种异常的,如果出现了相应的异常,将由调用者来处理,声明了异常不一定会出现异常
9.思考题
int类型与char类型的数组输出值是什么?
1
2
3
4
5 int[] arr = new int[]{1,2,3};
System.out.println(arr); //输出地址值
char[] arr = new arr[]{'a','b','c'};
System.out.println(arr); //输出abc多态(性)是编译时行为还是运行时行为?
- 答案:运行时行为
- 证明:可以通过系统生产随机数的方式,生成1调用dog,生成2调用cat,否则调用fish。在编译时我们不知道会调用哪个,只有在方法调用的那一刻才知道new的是谁,再进行调用方法。
包装类的思考题
1
2
3
4
5
6
7
8
9
10
11
12
13 public void test1(){
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1); //1.0,因为在编译的时候,两者的类型必须统一,不统一则会报错,所以前面的new Integer(1)需提升
}
public void test2(){
Object o2;
if(true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2); //1,
}关于final的思考题
1
2
3
4
5
6 public class Something(){
public int addOne(final int x){
//return x++; //错误,编译都过不了,因为x的值发生了改变
return x+1; //编译可以通过,x的值没有发生改变
}
}关于接口的思考题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 interface A{
int x = 0;
}
public class B{
int x = 1;
}
public class C extens B implements A{
public class X(){
System.out.println(x); //编译报错:The field is ambiguous
System.out.println(super.x); //1
System.out.println(A.x); //0 直接用A调用,x是全局常量
}
public static void main(String[] args){
new C().X();
}
}
10.知识拓展
对比数据类型的转化与多态的转换
- 较高级的基本数据类型 ————> 较低级的基本数据类型:强制类型转换,如double转int
- 较低级的基本数据类型 ————> 较高级的基本数据类型:自动类型转换,如int转double
- 父类(如Person) ————> 子类(如student):向下转型,需要使用instanceof进行判断
- 子类(如student) ————> 父类(如Person):向上转型,多态
- 风险:数据类型强制转换会导致精度有所损失,多态的强制转换可能会出现ClassCastException异常
设计模式——单例设计模式
- 定义:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
- 整体思路:- 如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
- 单例模式的饿汉式
- 实现步骤
- 私有化类的构造器(避免在外部new一个构造器)
- 内部创建类的对象
- 提供公共的静态的方法,返回类的对象(get方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 public class singletonTest1{
public static void main(String[] args){
//二者是同一个对象,因为调用的是static静态的方法
Bank bank1 = Bank.getIntance();
Bank bank2 = Bank.getIntance();
System.out.println(bank1 == bank2);//true
}
}
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象
//4.在提供了静态的方法之后,需要将此属性设置为静态的
//2. private Bank instance = new Bank();
private static Bank instance = new Bank();
//3.提供公共的静态的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}- 单例模式的懒汉式
- 实现步骤
- 私有化类的构造器(避免在外部new一个构造器)
- 声明当前类的对象,没有初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 public class singletonTest2{
public static void main(String[] args){
Order order1 = Order.getInstance;
Order order2 = Order.getInstance;
System.out.println(order1 == order2);//true
}
}
class Order{
//1.私有化类的构造器
private Order(){
}
//2.声明当前类的对象,并没有初始化
//4.在提供了静态的方法之后,需要将此属性设置为静态的
//2. private Order instance = null;
private static Order instance = null;
//3.声明一个public、static的返回当前类对象的方法
public static Order getInstace{
if(instance == null){
instance = new Order();
}
return instance;
}
}- 区分饿汉式与懒汉式
- 饿汉式
- 优点:饿汉式是线程安全的
- 缺点:对象加载时间过长
- 懒汉式
- 优点:延迟对象创建
- 缺点;目前的写法线程不安全,还可以修改
多态的应用:设计模式——模板方法设计模式
- 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式
- 解决的问题:
- 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现
- 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式
接口的应用:代理模式(Proxy)
- 代理模式:为其他对象提供一种代理以控制对这个对象的访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public class NetWorkTest(){
public static void main(String[] args){
Server server = new Server();
//server.browse();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse();
}
}
interface NetWork(){
public void browse();
}
//被代理类:
class Server implements NetWork(){
public void browse(){
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NetWork(){
//属性初始化:
priate NetWork work;
public ProxyServer(NetWork work){
this.work = work;
}
public void check(){
System.out.println("联网之前的一些检测工作");
}
public void browse(){
check();
work.browse();
}
}
- 应用场景:
- 安全代理:屏蔽对真实角色的直接访问
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
- 分类:
- 静态代理(静态定义代理类)
- 动态代理(动态定义代理类)接口的应用:工厂模式
- 实现了创建者与调用者的分离。即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
- 分类:
- 无工厂模式
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
对属性可以赋值的位置
- a.默认初始化
- b.显示初始化
- c.构造器中初始化
- d.有了对象之后,可以通过“对象.属性”或“对象.方法”的方式,进行赋值
- e.在代码块中赋值
- 执行顺序:a - b / e - c - d (b与e的执行看其先后顺序,谁在前先执行谁)
Eclipse快捷键
- 补全代码:alt + /
- 快速修复:ctrl + 1
- 批量导包:ctrl + shift + o
- 上下移动代码:alt + up/down
- 查看源码:ctrl + 选中的指定结构
- 格式化代码:ctrl + shift + f
- 添加项目备注:/** + enter
Eclipse常用设置
- 自动补全代码: window-preference-Java-Editor-Content Assit中,将Auto activation triggers for Java改为
abcdefghijklmnopqrstuvwxyz.