概述
java基础学习笔记
寒假开始学习java,记录一下。
常见dos指令
dir 列出
md 创建目录
rd 删除目录
cd 进入指定目录
cd.. 返回上级
cd\ 返回根目录
del 删除文件
java两种核心机制
- jvm
- gc
垃圾回收机制
c,c++,由程序员回收,手动编写代码回收
优点:能够在内存不使用时快速回收,准确高效;缺点:容易失误出现bug,例如忘记编写回收内存的代码,内存一直不回收。
java,垃圾回收是自动,开了一个系统集线程自动去检测哪些内存不用了然后回收掉
优点:自动的,意味着不会出现忘记回收;缺点:回收不及时。
一般的观点是,宁可回收不及时但是一定要回收,使用自动的垃圾回收恒合适
环境变量配置
- 设置JAVA_HOME 解压jdk的目录。
- 设置CLASSPATH .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar
- 在path中加入两个新的配置 %JAVA_HOME%\bin %JAVA_HOME%\jre\bin
修改jdk版本直接修改JAVA_HOME中jkd 目录即可。
不同版本java变换
使用压缩版的jdk,根据情况解压不同版本,改变JAVA_HOME来使用
JDK:java开发工具包
JRE:java运行环境
dos编译运行
javac Test.java
java Test
main
Java应用程序的执行入口是main()方法。
它有固定的书写格式:
public static void main(String[] args) {...}
java语言严格区分大小写。
声明为public的主类应与文件名一致。
注释
单行注释
//
多行注释
/* */
文档注释
/**
*@author
*@version
*/
命名规范
关键字:专门用途的字符串,所有字母为小写
保留字:以后可能会作为关键字的单词,尽量不要使用
标识符:数字不可以开头;不可使用关键字保留字,但可包含它们;不能有空格;区分大小写;长度不限;可使用_和$
包名:多单词组成时所有字母都小写:xxxyyyzzz
类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz
常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
notepad++使用技巧
- 按住alt建可以竖行拖动。
变量
作用域
一对中括号 {} 有效
声明
必须由初始值。
变量的类型
分为基本数据类型(8种)和引用数据类型。
基本数据类型:
整数类型:
byte b = 126
int i = 1
short s = 1
long l = 3l ,==long类型赋值时要在后面加上一个字母l。==
浮点型:
- 单精度float 四字节
- 双精度double
java的浮点型默认为double型,声明float型常量须后加f或F。
double d = 1.22
float f = 1.22f
字符类型:
用 单引号‘’ 括起来的单个的字母、数字、符号。
如何定义一个单引号:
char c = '\''
转义字符 \
布尔型:
true 或者false == 无null==
字符串
String定义字符串,由0到多个字母数字符号共同组成的一个串,用**双引号“ ”**括起来,属于引用类型
String s = "hello world“ ;
String s1 = "he" + "ll" +"o" ; //字符串可以用”+“衔接
引用类型都可以以null作为值,初始化时可以用null作为值,string也是这样。
String类是一个典型的不可变类,String对象创建出来就不可能被改变。
创建出的字符串将存放在数据区,保证每个字符串常量只有一个,不会产生多个副本。而别的数据类型可以有多个相同的值存在。
假设"hello"的内存地址xxxxx,声明s0变量时给s0赋值"hello"实际上让s0变量引用"hello"的内存地址xxxxx;
当我们再声明变量s1也赋值"hello"的时候实际上也是直接把已经存在的"hello"的内存地址给s1引用。
数字类型的运算规则
- 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。数字类型的从小到大分别是byte、short、int、long、float、double。
- 数字类型的运算中,多个相同类型变量参与的运算,变量要先转换为相对应的数据类型的默认类型(比如两个byte类型的变量相加,会先把两个byte类型的变量转换成默认的int类型之后再计算,得到的结果是int类型)。这种情况适用于变量的数据类型的容量比默认类型的容量小,(比如byte,short,都比int小)
- byte,short,char之间不会相互转换,他们三者在计算时首先转换为int类型。
- 当把任何基本类型的值和字符串值进行连接运算时(+),基本类型的值将自动转化为字符串类型。
System.out .println(3+4+“Hello!”); //输出:7Hello!
System.out.println(“Hello!”+3+4); //输出:Hello!34
System.out.println(‘a’+1+“Hello!”); //输出:98Hello!
System.out.println(“Hello!”+‘a’+1); //输出:Hello!a1
System.out.println('*' + '\t' +'*'); //输出:93
System.out.println("*" + '\t' +'*'); //输出* *
强制类型转换
将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符(),但可能造成精度降低或溢出。
int k = 7;
byte i = (byte) k ;
boolean类型不可以转换为其它的数据类型。
字符串不能直接转换为基本类型,但通过基本类型对应的包装类则可以实现把字符串转换成基本类型。
运算符
算术运算符
当整数除以整数时,结果小数部分舍去。
取模:取余数,对负数取模,负号可忽略不计,被模数是负数则不可忽略
5%-2=1 -5%2=-1
i++ 先运算后取值
++i 先取值后运算
赋值运算符
=:支持连续赋值, a=b=c=0
+= ,-= ,*= ,/= ,%=使用这些符号时,变量在参与运算时会把结果强制转换为结果的类型。
i= i+2 等同于 i+=2
比较运算符
==,! =,>,<,>=,<=结果都是Boolean型
不可连续使用,如2<x<6
逻辑运算符
&与,|或,!非,^异或(两个条件不一样时成立),
&&短路与:结果同 与 一致
||短路非:结果同 或 一致
&左边无论真假,右边都参与运算,&&左边为假,右边不运算。
||同理。
位运算符
无<<<
三元/目运算符
格式:
(条件表达式)?语句1 : 语句2
为true执行1,为false 执行2
int k = i>0 ? 1 : 0;
int k = m>n? (m>k?m:k):(n>k?n:k);//可加括号进行嵌套
运算符的优先级
使用scanner获取键盘输入
import java.util.Scanner;
public class TestScanner{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你的年龄");
String name = scanner.nextLine();
}
}
程序的流程控制
顺序结构
分支结构
if-else语句
if(条件语句){
表达式1
}
else if{
表达式2
}
……
……
else {
表达式n
}
switch语句
表达式的返回值必须是下述几种类型之一:byte,short,char,int,枚举,String。
switch(变量){
case 常量1:
语句1;
break;
case 常量2:
....
break;
......
......
default: //用于输入内容不在上述情况中时
}
循环结构
for(int i = 1;i<=100;i++){
语句;
}
while(i<=100){
i++;
}
do{
语句
i++;
}while(i<=100);
特殊控制语句
break :终止当前所在的循环
continue :跳出当前所在循环一次,直接进入下次
return: 结束整个方法
数组
一维数组
声明
//type var[];
//type[] var;
int a[];
int[] b;
Mydate[] c;//对象数组
动态初始化
int[] arr = new int[3];
arr[0] = 3;//数组下标从0开始
arr[1] = 4;
arr[2] = 6;
静态初始化
int a[] = new int[]{3,9,8};
int[] a = {3,9,8};
数组元素的引用
- 定义并用运算符new为之分配空间后,才能引用数组种的每个元素。
- 数组元素的引用方式:数组名【数组元素下标】;
数组元素下标可以位整型常量或者整型表达式。a[3], b[i], c[6*i]
数组元素下标从0开始。 - 每个数组都有一个属性length指明他的长度。a.length
数组初始化后,长度不可变 - 数组有默认值,数字型为0,对象的默认型为null。
多维数组
二维数组:数组中的数组
动态初始化
int[][] arr = new int[3][2];//第一维长度为2,第一位每个元素长度为3
int[][] arr = new int[3][];//也可只定义第一维,第二维不定义。
int[][] arr = new int[][2];//非法
int[] x,y[];// x是一维数组,y是二维数组。
行数 arr.length
列数 arr[0].length
静态初始化
int[][] arr = new int[][]{{3,8,2},{2,3},{1,2,4,6}};
数组的常见算法
//foreach
for(int m:a){
System.out.println(m);
}
//数组的反转
int[] rra = new int [arr.length];
for(int i = 0;i < arr.length;i++){
int k = arr.length - i -1;
rra[k] = arr[i];
}
for(int i=0;i<arr.length;i++){
System.out.println(rra[i]);
}
//冒泡排序
int[] ii = new int[]{1,9,6,3};
for(int i = 0; i < ii.length-1;i++){
for (int j = 0;j < ii.length - 1 - i;j++){
if(ii[j]>ii[j+1]){
int a = ii[j];
ii[j] = ii[j+1];
ii[j+1] = a;
}
}
}
for(int i=0;i<ii.length;i++){
System.out.println(ii[i]);
}
数组操作的常见问题
数组越界异常
int[] arr = new int[2];
System.out.println(arr[2]);
空指针异常
int[] arr = null;
System.out.println(arr[0]);
面向对象
三大特征:封装、继承、多态
java类及类的成员
属性与方法。
类的基本写法
在同一个java文件中可以有多个class,但是只有一个public class。
public class Person {
//属性,成员变量;类的成员变量可以先声明,不用初始化,有一个默认值
String name; //默认值为null
int age;//默认值为0
//行为,方法,函数
//void 指无返回值
public void showName(){//方法命名遵从驼峰原则
System.out.println("姓名" + name);
}
public int getAge(){//如果有返回值,则方法体最后一行一定是return
return age;
}
public void habbit(String a){
System.out.println("此人的爱好为"+a);
}
}
对象的创建与使用
public class Test4 {
public static void main(String[] args){
//实例化Person类
Person person = new Person();//声明一个Person类型的变量
//new Person()就是实例化
person.name = "李四"; //赋值
person.showName();//方法的调用
int i = person.getAge();//有返回值的方法
System.out.println(i);
person.habbit("唱跳rap");
}
}
类的属性
修饰符 类型 属性名 = 初值
修饰符:private 只能由该类的方法访问
public 能被任意类的方法访问
实例变量无static:在类实例化为对象后才能使用
类变量有static,不需要实例化就可以使用。
方法小括号里的为形参,形参间以,间隔。
方法的重载
**概念:**在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者类型不同即可。
**特点:**与返回值类型无关,只看参数列表,且参数列表必须不同。
形参个数可变的方法
用数组的方式来传递可变个数的参数,如果没有参数则要定义一个空数组或者null
public void printLnfo(String[] args){//打印一个人的信息。来源于方法的参数
//给方法传递的参数数目不清楚
//需要可变个数的形参
for(int i = 0; i < args.length ;i++){
System.out.println(args[i]);
}
}
java特有的…方式来传递,参数的使用与数组相同,没有参数不填即可。
如果一个方法有多个形参,可变形参…一定要放在所有形参的最后。(int age,String… args)
public void printLnfo1(String... args){//参数类型可变,int...等等
for(int i = 0; i < args.length;i++){
System.out.println(args[i]);
}
}
//使用... 的方法
p3.printLnfo1("lisi","23","nan");
//或者
String[] ss1 = new String[]{"江苏省","141","321"};
p3.printLnfo1(ss1);
方法的传递
方法的传递只是值的传递!!!
如果方法的形参是基本数据类型,那么实参向形参传递参数时,就是之间传递实参的值。
public class Test6 {
public static void swap(int i){
i = 6;
System.out.println("swap.i :"+ i );
}
public static void main(String[] args) {
int a = 0;
swap(a);
System.out.println("main.a :"+ a );
}
}
//a的值并没有变
形参是对象
如果方法的形参是对象,那实参向形参传递函数时,传递的值是实参在栈内存中的值,也就是引用对象在栈中的地址。
基本数据类型都是保存在栈内存中的,引用对象在栈内存中保存的是引用对象的地址。
软件包
与文件夹的概念类似
包的存在是为了便于文件管理
包有层次结构, 用.来分隔 顶层.第二层
包通常用小写单词
import day06.test.Person;//调用包
import day06.test.*;//引用整个test
封装与隐藏
使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。
对不能让调用者随意使用的属性进行封装和隐藏,使用private声明为私有属性。
private int age;
//再通过方法来对属性进行操作
public void setAge(int a){
if(a <= 150 && a >= 0){
age = a;
}
else{
System.out.println("输入正确年龄。");
}
}
四种访问权限修饰符
private: 只能在类内部使用
无修饰符: 类内部和包内部可以使用
protected: 类,包,子类可以使用
public: 任意地方可以使用
对与class的修饰符只有 public 或者 缺省
类的构造器
public class Person(
public Person(){
//在里面可以定义对象的值
}
}
- 无需写出,默认存在;如果显示定义了则不会使用默认的
- 修饰符与名称与类一致
- 一个类可以创建多个重载的构造器
- 父类的构造器不可被子类继承
- new 对象就是调用构造器,Person p = new Person()
- 构造器重载使得对象的创建更加灵活,参数列表必须不同。
关键词this
this表示当前对象,可以调用类的属性、方法和构造器
他在方法内部使用,即这个方法所属对象的引用
他在构造器内部使用,表示该构造器正在初始化的对象。
//1.当形参与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表明该变量时类成员
public Person2(int age,String name){
this.age = age;
this.name = name;
}
//2.在任意方法内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性
public void showInfo(){
System.out.println(this.name);
}
//3.this可以作为一个类中,构造器相互调用的特殊格式
public Person2(){
}
public Person2(String name){
this(); //调用上面的无参构造
this.name = name;
}
- 使用this()必须放在构造器首行!
- 使用this调用本类中其他的构造器,保证至少有一个构造器是不用this的。(实际上就是不能出现构造器自己调用自己)
JavaBean
JavaBean是一种java语言写成的可重用组件
有以下标准:
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,属性一般私有,且有对应的get、set方法
鼠标右键-------Source—Generate getters and setters
继承
多个类找出共性,然后写出父类,
比如 人:学生、老师、工人,
每个子类只需写自己的特有代码。
//使用extends
public class Student extends Person{ //Student是Person的子类
}
不要仅为了获取其他类中的某个功能而去继承
eg:Dog也有name,age,sex等属性,但不能因此继承Person这个类
子类继承了父类的方法和属性
子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法
java只支持单继承,一个子类只能有一个父类
一个父类可以有多个子类
但可以多层继承 爷 父 子 孙
方法的重写override
子类可以对从父类继承的方法进行改造
- 重写的方法必须与原方法具有相同的方法名称、参数列表和返回值类型,只是重写方法体的代码
- 重写方法不能使用比原方法更严格的访问权限
- 重写和原方法必须同时为static或非static
关键字super
- 可以用于访问父类中定义的属性
- 可以用于调用父类中定义的成员方法
- 可以用于在子类构造方法中调用父类的构造器
- 当子父类出现同名成员时,可以用super进行区分
- super的追溯不仅限于直接父类
- super与this用法相像,this代表本类对象的引用,super代表父类的内存空间的标识。
- 在父类只有有参构造时,子类必须显示的构建一个构造来调用父类的有参构造
- this访问本类中的属性,如果本类中没有,则从父类继续查找
- 子类中,通过this或者super调用构造器,只能使用一个
public ManKind(int sex, int salary){
this.sex = sex;
this.salary = salary;
}
public Kids(int sex, int salary){
super(sex, salary);//super要写在第一行
}
简单类对象的实例化过程
没看
多态性
方法的多态性
重载与重写
对象的多态性
-
java引用变量有两个类型:编译时类型和运行时类型。
编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
若编译时类型和运行时类型不一致,就出现多态 -
子类的对象可以替代父类的对象使用
子类可以看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型。
Person p = new Person();
Person e = new Student(); // Person类型的变量e,指向Student类型的对象
- 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能访问子类中添加的属性和方法
Student m = new Student();
m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”; //非法,Person类没有school成员变量
- 但可以调用子类中的方法,必须存在与方法的重写之上。
// 正常的方法调用
Person p = new Person();
p.getInfo();
Student s = new Student();
s.getInfo();
// 虚拟方法调用(多态情况下)
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
子类继承父类
- 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中
- 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
instanceof操作符
- x instanceof A:检验x是否为类A的对象,返回值为boolean型。
- 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
- 如果x属于类A的子类B,x instanceof A值也为true。
Object类
Object类是所有java类的根父类,基类
如果在类的声明中未使用extends关键字指名其父类,则默认其父类为Object类
p.equals(s) //判断p和s是否为同一个对象
p.hashCode()//获取哈希码
p.toString() //打印对象地址
对象类型转换(Casting)
基本数据类型
int i = 10;
long l = i ;//小的数据类型自动转换为大的。
long l1 = 10l;
int i1 = (int)l;//大的数据类型强制转化为小的
造型
对java对象的强制类型转换
- 从子类到父类的类型转换可以自动进行。
Student s = new Student();
Person p = s;
- 从父类到子类的类型转换要强制进行。
Person p = new Person();
Student s = (Student) p ;
- 无继承关系的转换是非法的。
public void method(Person e){
if(e instanceof Student){
Student s = (Student)e;//形参是p,所以还得强制转换一下。
s.getSchool();
}
else{
e.test();
}
}
==操作符与equals方法
==
- 基本值变量的比较。
- 引用类型比较引用(是否指向同一个对象)
Person p1 = new Person();
Person p2 = new Person();
Person p3 = p1;
Test t = new Test();
System.out.println(p1 == p2); //false
System.out.println(p1 == p3); //true
System.out.println(p1 == t);//报错。
用“==”进行比较时,符号两边的数据类型必须兼容,否则编译出错。
equals
只能比较引用类型,作用与==相同。
System.out.println(p1.equals(p2) );//false
特例:当equals()方法进行比较时,对类File、String、Date及包装类(Wrapper Class)来说,是在比较类型及内容而不考虑引用的是否是同一个对象。
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2);//false
System.out.println(s1.equals(s2));//true
String对象的创建
字面量创建String对象
比new创建方式省内存
String s1 = "abc";//常量池中添加“abc”对象,返回引用地址给s1对象
String s2 = "abc";
//通过equals()判断常量池中已有值为abc的对象
//将已有“abc”对象的地址也返回给s2对象
System.out.println(s1 == s2); //true
new创建String对象
String s3 = new String("def");
//在常量池中添加“def”对象,
//在堆中创建值为“def"的对象s3,返回指向堆中的s3的引用
String s4 = new String(“def”);
//常量池中已有值为“def”的对象,不做处理,
//在堆中创建值为“def"的对象s4,返回指向堆中的s4的引用
System.out.println(s3 == s4);//false
字面量相加
String s5 = "x" +"y";//将结果添加进常量池,再返回应用地址。
String s6 = new String("1") + new String("2") +new String("2");
//通过StringBuilder实现,在常量池中添加“1”和“2”两个对象
//在堆中创建值为“112”的对象,把引用地址给s6。
包装类
主要用于基本数据类型和字符串的转化
基础数据类型包装成包装类的实例 ----装箱
1. 通过包装类的构造器实现:
int i = 500;
Integer t1 = new Integer(i);
2. 通过字符串参数构造包装类对象:
Float f = new Float("4.56");
Long l = new Long("asdf");//可以编译但运行错误,必须得是数字
获取包装类对象中包装的基本类型变量 ----拆箱
Integer i = new Integer(i);
int i0 = i.intValue();
JDK1.5后,支持自动装箱,自动拆箱。但类型必须匹配
Integer i1 = 112;//自动装箱
int i2 = i1;//自动拆箱
boolean b = new Boolean("true");//自动拆箱
字符串转换为基本数据类型
1. 通过包装类的构造器实现:
int i = new Integer("12");
2. 通过包装类的parseXxx(String s)静态方法:
float f = Float.parseFloat("12.1");
int i = Integer.parseInt("123");
基本类型转化为字符串
1. 调用字符串重载的valueOf()方法
String iste = String.valueOf(123);
2.更直接的方法
String intStr = 5 + "" ;
toString
MyDate d1 = new MyDate(1998,8,27);
System.out.println(d1.toString());//输出当前对象的内存地址
System.out.println(d1);//相当于toString
如果想输出类的其他信息,重写toString
关键字static
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。 我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。
static String country;
类变量,不用实例化,直接通过类名.属性名就可以使用
被这个类的实例化对象共享
也叫静态变量
方法也可以用static修饰,类名.方法名直接使用
public static void test(){
.......
}
判断是否是空字符串
public static boolean isEmpty(String s){
boolean flag = false;
if(s != null && !s.equals("")){
flag = true;
}
return flag;
}
static 方法内部不能有this和super,只能访问类的static属性
单例设计模式
- 设计模式就是在我们实际编程过程中,逐渐总结出的一些解决问题的套路。
- 单例指只有一个实例化对象。
- 使用单例模式解决问题,一般都是在new对象太费时间内存或者是频繁new新对象没必要。
饿汉式单例
public class Single {
//私有的构造方法,不能通过new直接来创建对象
private Single(){
}
//私有的Single类型 的类变量
private static Single s = new Single();
public static Single getInstance(){
return s;
}
public static void main(String[] args) {
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
Single s3= Single.getInstance();
Single s4 = Single.getInstance();
Single s5 = Single.getInstance();//都指向s
}
}
懒汉式
public class Single1 {
//私有化构造方法
private Single1(){
}
private static Single1 s1 = null;
public static Single1 getInstance(){
if(s1 == null){
s1 = new Single1();
}
return s1;
}
public static void main(String[] args) {
Single s1 = Single1.getInstance(); //null
Single s2 = Single1.getInstance(); //new Single1
Single s3= Single1.getInstance();//new Single1
Single s4 = Single1.getInstance();//new Single1
Single s5 = Single1.getInstance();//new Single1
}
}
main方法
public static void main(String[] args){
}
初始化块
对java对象进行初始化
程序的执行顺序:
- 声明成员变量的默认值
- 显示初始化,多个初始化块依次被执行(先执行静态再执行非静态,同级别下按先后顺序执行)
- 构造器再对成员进行赋值操作
非静态代码块:没有static修饰的代码块
- 可以有输出语句。
- 可以对类的属性声明进行初始化操作。
- 可以调用静态和非静态的变量或方法。
- 若有多个非静态的代码块,那么按照从上到下的顺序依
次执行。 - 每次创建对象的时候,都会执行一次。且先于构造器执行
静态代码块:用static 修饰的代码块
- 可以有输出语句。
- 可以对类的属性声明进行初始化操作。
- 不可以对非静态的属性初始化。即:不可以调用非静态的属
性和方法。 - 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态代码块的执行要先于非静态代码块。
- 静态代码块只执行一次
实际开发中static静态代码块用在初始化类的静态属性
在匿名内部类中用代码块代替构造
public class Person {
String name;
public Person(){
this.name = "wjw";
System.out.println("执行的构造方法");
}
//非静态代码块
{
System.out.println("非静态代码块");
}
//静态代码块
static{//只能使用静态修饰的属性和方法。
System.out.println("静态代码块");
}
public static void main(String[] args) {
new Person();
}
}
/*
运行结果:
静态代码块
非静态代码块
执行的构造方法
*/
final关键字
final表示最终,修饰类、属性和方法。
- final修饰类,不能被继承
- final修饰方法不能被子类重写
- final编辑的变量为常量。名字大写,若为多个单词,则有_连接,只能被赋值一次。
- final static修饰的变量为全局常量。
抽象类
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
- 用abstract修饰类和方法。
- 抽象方法只有方法的声明,没有方法的实现,以;结束。
abstract int method(int a);
- 含有抽象方法的类必须被声明为抽象类。
- 抽象类不能被实例化,抽象类是用来继承的。抽象类的子类必须重写父类的抽象方法并提供方法体。若没有重写全部的抽象方法,则仍为抽象类。
- 不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法。
模板方法设计模式
抽象类
接口interface
- 接口是抽象方法与常量值的定义的集合。
- 是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
- 实现多重继承的效果
- 一个类可以实现多个接口,接口也可以继承其他接口。
接口的特点:
- 用interface来定义。
- 接口中的所有成员变量都默认是由public static final修饰的。
- 接口中的所有方法都默认是由public abstract修饰的。
- 接口没有构造器。
- 接口采用多层继承机制。
public interface Test{
int i = 1;//等同public static final int i = 1;
void test();//等同于public abstract void test()
}
//类可以实现多个接口,用,分割
public class TestInter implements Test,TestIn1{
}
//接口可以继承接口
public interface TestIn2 extends TestIn1{
}
- 接口的主要用途就是被实现类实现。
- 实现接口的类必须提供接口中所有方法的具体实现内容方可实例化。否则,该类仍然为抽象类。
- 一个类既有继承又有实现,则先写extends后写implements
public class Test extends Test1 implements TestIn{
}
父类需要稳定的抽象,如果父类进行修改则会影响子类。所以当需要给父类新增方法的时候,只能新建一个接口,在接口上扩展方法。其他需要的子类自行去实现接口。
可以用接口接受一个new对象
Cooking c = new SCTeacher();
工厂方法(FactoryMethod)
FactoryMethod模式是设计模式中应用最为广泛的模式,在面向对象的编程中,对象的创建工作非常简单,对象的创建时机却很重要。FactoryMethod解决的就是这个问题,它通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好的解决了这种紧耦合的关系。
内部类
Inner class作为类的成员:
- 可以声明为final的
- 和外部类不同,Inner class可声明为private或protected;
- Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;
Inner class作为类:
- 可以声明为abstract类 ,因此可以被其它的内部类继承
- 【注意】非static的内部类中的成员不能声明为static的,只有在外部类或static的内部类中才可声明static成员。
public class Test3 {
int i;
public int z;
private int k;
class A{
int i;
public void setTest3Fileds(){
Test3.this.i = 1;
Test3.this.z = 2;
Test3.this.k = 3;
}
public void set(){
this.i = 10;
}
}
//如果内部类是static的,就不能使用外部类的非static的成员
static class B{
}
abstract class C{
}
class D extends C{
}
public void setInfo(){
new A().setTest3Fileds();//外部的类要用自己的内部类的方法,得先new内部类的对象
}
public void showInfo(){
System.out.println(this.i);
System.out.println(this.z);
System.out.println(this.k);
}
public static void main(String[] args) {
Test3 t = new Test3();
t.setInfo();
t.showInfo();
}
}
内部类实现多重继承
public class Test4 {
public static void main(String[] args) {
A a = new A();
a.testB();
a.testC();
}
}
/**
* 现在类A想同时获得类B和类C的方法,并且重写
* 可以使用内部类来变相的实现类的多重继承,可以同时继承多个类
* @author lby
*
*/
class A{
public void testB(){
new InnerB().testB();
}
public void testC(){
new InnerC().testC();
}
private class InnerB extends B{
@Override
public void testB() {
System.out.println("这是重写之后的testB方法");
}
}
private class InnerC extends C{
@Override
public void testC() {
System.out.println("这是重写之后的testC方法");
}
}
}
class B{
public void testB(){
}
}
class C{
public void testC(){
}
}
匿名内部类
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
interface A{
public abstract void fun1();
}
public class Outer{
public static void main(String[] args) {
new Outer().callInner(new A(){
//接口是不能new但此处比较特殊是子类对象实现接口,只不过没有为对象取名
public void fun1() {
System.out.println(“implement for fun1");
}
});// 两步写成一步了
}
public void callInner(A a) {
a.fun1();
}
}