Java 学习日记(1)
Java 学习日记(1)
准备
JDK:Java 开发工具包。
JDK 是提供给 Java 开发人员使用的,其中包含了 Java 的开发工具,也包含了 JRE。
JRE:Java 运行环境。
包括 Java 虚拟机和 Java 程序所需要的核心类等,如果想要运行一个开发好的 Java 程序,计算机只需安装 JRE 即可。
安装:
下载 JDK。
配置环境变量
JAVA_HOME=D:\JAVA
Path=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
Classpath=.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
HelloWorld
/*
在一个java源文件中可以声明多个class,但是,只能最多有一个声明为public的,且必须和文件名相同
程序的入口是main方法,格式固定
System.out.println() 输出语句,换行
System.out.print()
每行执行语句以;结束
编译后,会生成一个或多个字节码文件,字节码文件的文件名与java源文件中的类名相同。
*/
class HelloWorld{
public static void main(String[] args){
System.out.println("Hello World");
}
}
编译运行:
javac HelloWorld.java
java HelloWorld
1、编译得到字节码。 2、运行。
注释
单行注释
/*
1、单行注释
2、多行注释(不可嵌套使用)
3、文档注释(Java 特有)
注释内容可以被JDK提供的工具Javadoc解析,生成一套以网页文件形式的说明文档
*/
/**
* 文档注释
* @author an
* @version v1.0
*/
public class HelloJava {
/**
* 如下的方法是main方法
*/
public static void main(String[] args){
// 单行注释
/*
多行注释
*/
System.out.println("注释");
}
}
生成 javadoc
javadoc -d myHello -encoding utf-8 -author -version HelloJava.java
Java API 文档
良好编程风格
- 正确使用注释
- 使用文档注释来注释整个类或整个方法
- 如果注释方法中的某个步骤,使用单行或行尾注释
- 正确的缩进和空白
- 使用一次tab操作符进行缩进
- 运算符两边习惯加上一个空格
关键字和保留字
关键字:被Java语言赋予了特殊含义,用作专门用途的字符串。
保留字:现有Java版本尚未使用,但以后版本可能会作为关键字使用。
标识符
标识符:Java对各种变量、方法和类等要素命名时使用的字符串称为标识符。
1)由26个英文字母大小写,0-9,_或$
2)数字不能开头
3)不能使用关键字和保留字
4)严格区分大小写
5)不能有空格
命名规范:
1)包名:多单词组成时所有字母都小写
2)类,接口名:多单词组成时,所有单词的首字母大写,XxxYyy
3)方法、变量名:第一个单词首字母小写,第二个单词起每个单词首字母大写
4)常量名:所有字母大写,多单词通过下划线连接
变量
内存中的一个存储区域,包含变量类型,变量名和存储的值。
变量必须先声明,后使用。
变量定义在作用域内,在作用域中,它是有效的。
数据类型
基本数据类型:
- 数值型(整数型 :byte,short,int,long 浮点数型 :float,double)
- 字符型(char)
- 布尔型(boolean)
引用数据类型
- 类(class)
- 接口(interface)
- 数组([])
在类中声明的位置
- 成员变量
- 局部变量
整数类型
java 的整型变量默认为int型,声明long型常量须后加 'l' 或 'L'
byte(1字节=8bit)\short(2字节)\int(4字节)\long(8字节)
浮点型
float 单精度,大约可以精确到7位有效数字 4字节
double 双精度,精度是float的两倍 8字节
默认位double类型,声明float型常量,须后加'f'或'F'。
字符类型
char 2字节,定义时使用一对''
布尔类型
只能取 true、false
类型转换
前提:只讨论7种基本数据类型,不包含boolean类型
1、自动类型提升:
当容量小的数据类型变量与容量大的数据类型变量运算时,结果自动提升为容量大的类型
此时容量大小指的是表示数的范围大小,不是占用内存空间大小。
2、强制类型转换
需要使用强转换符:int a=(int)b;
可能导致精度损失
变量运行的两个特殊情况。
float l=2.3; //报错
long i=2; //没问题
字符串类型: String
1、属于引用类型的数据类型
2、声明String类型的变量时,使用一对""
3、String 可以和8种基本数据类型做运算,且运算只能是连接运算"+"。
进制表示
int num1=0b110; //二进制
int num2=110; //十进制
int num3=0128; //八进制
int num4=0x110A; //十六进制
运算符
算术运算符
+ - * / % ++ --
赋值运算符
=
+= -= *= /= %=
比较运算符
== != < > <= >=
instance of // 检查是否是类的对象
逻辑运算符
& | !
&& || ^
位运算符
<< >> >>>无符号右移
& | ^ ~
三元运算符
(条件表达式)?表达式1:表达式2;
优先级
程序流程控制
分支结构
if(){
}
else if{
}else{
}
使用 Scanner 从键盘获取输入:
/*
从键盘获取不同类型的变量:需要使用Scanner类。
具体实现步骤
1、导包:import java.util.Scanner;
2、Scanner 实例化:Scanner sc=new Scanner(System.in);
需要按照响应的方法来输入指定类型的值,若输入类型与要求类型不匹配时,会报异常。
*/
import java.util.Scanner;
public class ScannerTest {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
System.out.println(num);
// 对于char型的获取,没有提供相关方法,只能获取一个字符串
String gender = sc.next();
char genderChar = gender.charAt(0);
}
}
switch-case:
switch(number){
case 0:
System.out.println("zero"); break;
case1:
...
default:
...
}
循环语句
for(;;)
while(){
}
do{
}while();
break
for(;;)
for(;;)
break; // 只能结束内层
label:for(;;)
for(;;)
break label; // 可结束外层的循环
数组
package com.an.contact;
public class ArrayTest {
public static void main(String[] args) {
// 一维数组的声明和初始化
int[] ids; // 声明
ids = new int[]{1,2,3}; //静态初始化
// 动态初始化
// ids = new int[12];
String[] names = new String[3];
// 初始化
names[0] = "3";
names[1] = "a";
names[2] = "b";
// 数组长度,属性 length
int len = names.length;
// 遍历数组元素
for(int i=0;i<len;i++) {
System.out.println(names[i]);
}
// 数组元素的默认初始化值:0
// 数组的内存解析
//
//
//
// 二维数组声明和初始化
int[][] arr1 = new int[][] {{1,2,3},{1,2,3}};
String[][] arr2 = new String[3][2];
System.out.println(arr1[1][1]);
}
}
Arrays 工具类的使用
package com.an.contact;
import java.util.Arrays;
/**
* java.util.Arrays: 操作数组的工具类,定义了很多操作数组的方法
* @author an
*
*/
public class ArrayFunc {
// 1.boolean equals(int[] a,int[] b); 比较两个数组是否相等
public static void main(String[] args) {
int[] arr1 = new int[]{1,2,3};
int[] arr2 = new int[]{1,2,3};
System.out.println(Arrays.equals(arr1,arr2));
System.out.println(Arrays.toString(arr1));
// 填充
Arrays.fill(arr1,10);
// 排序
Arrays.sort(arr1);
// 二分查找
int index = Arrays.binarySearch(arr2, 3);
System.out.println(index);
}
}
常见异常
// ArrayIndexOutOfBoundsException 下标越界
// NullPointerException 空指针异常
//
类和对象
package com.an.contact;
public class PersonTest {
public static void main(String[] args) {
// 创建Person类的对象
Person p1 = new Person();
p1.name = "Tom";
p1.isMale = true;
//调用方法
p1.talk("Eng");
}
}
class Person {
// 属性
String name;
int age = 1;
boolean isMale;
// 方法
public void eat() {
System.out.println("人可以吃饭");
}
public void sleep() {
System.out.println("人可以睡觉");
}
public void talk(String language) {
System.out.println("人可以说" + language);
}
}
对象权限修饰
private: Java语言中对访问权限限制的最窄的修饰符,一般称之为“私有的”。被其修饰的属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。
default:即不加任何访问修饰符,通常称为“默认访问权限“或者“包访问权限”。该模式下,只允许在同一个包中进行访问。
protected: 介于public 和 private 之间的一种访问修饰符,一般称之为“保护访问权限”。被其修饰的属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包中也可以访问。
public: Java语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包访问。
匿名对象
package com.an.contact;
/**
* 创建的对象,没有一个显式赋给一个变量名,即为匿名对象。
* 匿名对象只能调用一次
* @author an
*
*/
public class PhoneTest {
public static void main(String[] args) {
Phone p = new Phone();
System.out.println(p);
p.sendEmail();
p.playGame();
// 匿名对象
new Phone().sendEmail();
}
}
class Phone {
double price;
public void sendEmail() {
System.out.println("发送邮件");
}
public void playGame() {
System.out.println("玩游戏");
}
}
方法重载
package com.an.contact;
/**
* 方法的重载
* 在同一个类中,允许存在一个以上的方法,只要它们的参数类型不同
* @author an
*
*/
public class OverLoadTest {
public void getSum(int i,int j) {
}
public void getSum(double i,double j) {
}
public void getSum(int i,int j,int k) {
}
// public int getSum(int i,int j) {
// 错误,不是重载
// }
}
可变个数的形参
JavaSE 5.0 提供了 Varargs 机制,允许直接定义能和多个实参相匹配的形参,从而,可以用一种更简单的方式,来传递个数可变的实参。
必须声明在末尾
在方法的形参种,最多只能声明一个可变形参
package com.an.contact;
public class MethodArgsTest {
public void show(int i) {
}
// 可变个数的形参
public void show(String ... strs) {
System.out.print(strs[0]);
}
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
test.show("a","b");
}
}
方法参数的值传递机制
Java 里方法的参数传递方式只有一种:值传递。即将实际参数的副本传入方法内,而参数本身不受影响。
-
形参是基本数据类型,将实参基本数据类型变量的“数据值”传递给形参。
-
形参是引用数据类型,将实参引用数据类型变量的“地址值”传递给形参。
构造器
package com.an.info;
/**
* constructor:
* 创建对象
* @author an
*
*/
public class PersonTest {
public static void main(String[] args) {
Person p = new Person("a");
Person p2 = new Person();
}
}
class Person {
String name;
int age;
public Person() {
System.out.println("构造中");
}
public Person(String n) {
name = n;
System.out.println("构造2");
}
public void eat() {
System.out.println("吃饭");
}
}
Java Bean
Java Bean 是一种使用 Java 语言写成的可重用组件。
1、类是公共的。
2、有一个无参的公共的构造器。
3、有属性,且有对应的get、set方法。
用户可以使用 Java Bean 将功能、处理、值、数据库访问和其他任何可以用 Java 代码创造的对象进行打包,并且其他的开发者可以通过内部的 JSP 页面、Servlet、其他JavaBean、applet 程序或者应用来使用这些对象。用户可以认为 JavaBean 提供了一种随时随地的复制和粘贴的功能,而不用关系任何改变。
UML 类图
this 的使用
package com.an.info;
/**
* constructor:
* 创建对象
* @author an
*
*/
public class PersonTest {
public static void main(String[] args) {
Person p = new Person();
p.setName("Jack");
System.out.println(p.getName());
}
}
class Person {
String name;
int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
this 修饰和调用构造器。
package、import 的使用
package 关键字的使用
1、为了更好的实现项目中类的管理,提供包的概念。
2、使用 package 声明类或接口所属的包,声明在源文件的首行。
3、包名属于标识符,需要遵循标识符的命名规则,“见名知意”。
4、每"."一次,就代表一层文件目录。
同一个包中,不能命名同一个接口或类。不同的包下可以。
JDK 中主要的包介绍。
-
java.lang - 包含一些 java 语言的核心类,如 String, Math, Integer, System 和 Thread,提供常用功能。
-
java.net - 包含执行与网络相关的操作的类和接口。
-
java.io - 包含能提供多种输入/输出功能的类。
-
java.util - 包含一些使用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
-
java.text - 包含了一些 java 格式化相关的类。
-
java.sql - 包含了 java 进行 JDBC 数据库编程的相关类/接口。
-
java.awt - 包含了构成抽象窗口工具集的多个类。
MVC 设计模式
MVC 是常用设计模式之一,讲整个程序分为三个层次:视图模型层,控制器层,与数据模型层。这种讲程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变得灵活清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
模型层:model 主要处理数据
- 数据对象封装 model.bean/domain
- 数据库操作类 model.dao
- 数据库 model.db
控制层:controller 处理业务逻辑
- 应用界面相关 controller.activity
- 存放 fragment controller.fragment
- 显示列表的适配器 controller.adapter
- 服务相关的 controller.service
- 抽取的基类 controller.base
视图层 view 显示数据
- 相关工具类 view.utils
- 自定义 view view.ui
import:为使用定义在不同包中的 java 类,需要使用 import 语句来引入指定包层次下所需要的类或全部类(.*)。import 语句告诉编译器到哪里去寻找类。
语法格式:
import 包名.类名;
Eclipse 中常见快捷键
alt+/ 自动提示
ctrl+1 快速修复
ctrl+shift+o 批量导包
alt+up alt+down 上下移动代码
ctrl+alt+up/down 复制指定行代码
ctrl+点击 ctrl+shift+t 查看源码
ctrl+shift+enter 切换到上一行代码空位
ctrl+enter 切换到下一行代码空位
alt+left alt+right 进入上一个/下一个编辑的页面
ctrl+shift+f 格式化代码
ctrl+k 查找下一个出现的位置
继承
好处
1、减小了代码的冗余,提高了代码的复用性
2、便于功能的扩展
3、为之后多态性的使用,提供了前提
package com.an.info;
public class ExtendsTest {
public static void main(String[] args) {
Student st = new Student();
st.breath();
}
}
class Student extends Person2 {
String id;
}
class Person2 extends Creature {
String name;
}
class Creature {
public void breath() {
System.out.println("呼吸");
}
}
Object 类
1、如果我们没有显式的声明一个类的父类的话,则此类继承于 java.lang.Object 类。
2、所有的类都直接或间接继承自object类
方法重写(override)
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
重写的规定
子类重写的方法名和形参列表和父类被重写的方法的方法名和参数列表相同
子类重写的权限修饰符不小于父类被重写的权限修饰符
子类和父类的同名相同参数的方法要么都是非static,要么都是static
package com.an.info;
public class ExtendsTest {
public static void main(String[] args) {
Student st = new Student();
st.breath();
}
}
class Student extends Person2 {
String id;
}
class Person2 extends Creature {
String name;
@Override
public void breath() {
System.out.println("人在呼吸");
}
}
class Creature {
public void breath() {
System.out.println("呼吸");
}
}
super 关键字
package com.an.info;
public class ExtendsTest {
public static void main(String[] args) {
Student st = new Student();
st.breath();
}
}
class Student extends Person2 {
String id;
}
class Person2 extends Creature {
String name;
@Override
public void breath() {
System.out.println("人在呼吸");
super.breath();
}
}
class Creature {
public void breath() {
System.out.println("呼吸");
}
}
使用父类的属性或方法
我们可以在子类的方法或构造器中,通过使用"super.属性"或"super.方法"的方式,显式调用父类中声明的属性或方法,但是,通常情况下,我们都习惯省略"super."
特殊情况:当子类和父类中定义了同名的属性时,要想在子类中调用父类中声明的属性,则必须显式的使用。
我们可以在子类的构造器中显示使用 super(形参列表)的方式,调用父类中声明的指定的构造器。
如果没有显式指定,则默认调用 super()。
子类对象实例化的全过程
1、从结果上看:
子类继承父类以后,就获取了父类中声明的全部方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
2、从过程中看:
通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了 java.lang.Object 类中空参的构造器位置。
虽然创建子类对象时,调用了父类的构造器,但是只创建了一个对象。
多态性
多态性,是面向对象中最重要的概念,在 Java 中的体现:
对象的多态性:父类的引用指向子类的对象
Java 引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
若编译时和运行时类型不一致,就出现了对象的多态性。
多态情况下,"看左边":看的是父类的引用(父类中不具备子类特有的方法)。
"看右边":看到是子类的对象(实际运行的是子类重写父类的方法)。
package com.an.accept;
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
Man man = new Man();
man.eat();
// 子类对象的多态性:父类的引用指向子类的对象
Person p2 = new Man();
// 多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法
// 虚拟方法调用
// 有了对象的多态性后,我们在编译器,只能调用父类声明的方法,但是在运行期,我们实际执行的是子类的方法
// 只能调用 person 里定义的方法
// 多态性使用前提 ① 类的继承关系 ② 方法重写 ③ 父类引用指向子类的对象
p2.eat();
}
}
多态不适用属性,只适用于方法。
虚拟方法调用(多态情况下):
子类中定义了与父类同名同参数的方法,在多态情况下,讲此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
编译时 e 为 Person 类型,而方法的调用是在运行时确定的,所以调用的是Student的方法。
向下转型
x instanceof A: 检验 x 是否为类A的对象,返回值为boolean型。
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果x属于类A的子类B,x instanceof A值也为true。
package com.an.accept;
import java.util.PrimitiveIterator.OfDouble;
/**
*
* @author an
*
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
Man man = new Man();
man.eat();
// 子类对象的多态性:父类的引用指向子类的对象
Person p2 = new Man();
// 多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法
// 虚拟方法调用
// 只能调用 person 里定义的方法
p2.eat();
System.out.println("**************");
// 不能调用子类特有的类型,编译时,p2是Person的类型
// 有了对象的多态性后,内存中实际是加载了子类特有的属性和方法的,
// 但是由于变量声明为父类类型,导致只能调用父类的方法
// 如何才能调用子类特有的方法和属性。
// 使用强制类型转换符、向下转型
// 使用强转时,可能出现 ClassCastException 的异常
Man m1 = (Man)p2;
/*
* a instance of A
* 为了避免出现异常,在向下转型之前,需要通过instanceof 判断
*/
if(p2 instanceof Woman) {
Woman w1 = (Woman)p2;
}
}
}
Object 类的使用
package com.an.accept;
/**
* 属性: 无
* Object 中只声明了一个空参的构造器
* Clone() 返回一个对象的副本
* equals() 比较两个对象是否相等
* finalize() 垃圾回收前调用
* hashCode() 返回hash值
* getClass() 获取当前对象的所属类
* notify()
* notifyAll()
* wait()
* toString()
* @author an
*
*/
public class ObjectTest {
public static void main(String[] args) {
Order order = new Order();
System.out.println(order.getClass().getSuperclass());
}
}
class Order {
}
equals 方法
== 和 equals 的区别
1、== 既可以比较基本类型也可以比较引用类型,对于基本类型就是比较值,对于引用就是比较内存地址。
2、equals 是属于 java.lang.Object 类里的方法,只适用于引用数据类型,如果该方法没有被重写过默认也是 ==。
3、通常情况下,重写 equals 方法,会比较类中的相应属性是否都相等。
package com.an.accept;
/**
* Object 类中 equals 方法的定义:
* public boolean equals(Object obj) {
* return (this==obj);
* }
* Object 类中定义的 equals 与 == 作用相同
* 像 String, Date, File, 包装类等都重写了 Object 的 equals 方法,重写后变为比较
* 两个对象的实体内容是否相同
* @author an
*
*/
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("人,吃饭");
}
public void walk() {
System.out.println("人,走路");
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
toString 方法的使用
package com.an.accept;
/**
* 当我们输出一个对象的引用,实际上是调用该类的 toString() 方法
* Object 类中 toString() 的定义
* public String toString() {
* return getClass().getName()+"@"+Integer.toHexString(hashCode());
* }
* String,Data,File,包装类都重写了toString方法
* @author an
*
*/
public class ToStringTest {
public static void main(String[] args) {
Person p = new Person("Tom", 21);
System.out.println(p.toString());
}
}
单元测试方法的使用
package com.an.accept;
import javax.xml.crypto.Data;
import org.junit.Test;
/**
* Java 中的 JUnit 单元测试
*
* 步骤:
* 1、选中当前工程-> 右键,build path-> add libraries -> JUnit4
* 2、创建 Java 类进行单元测试
* 要求:①此类是公共的 ②此类要提供一个公共的,无参的构造器
* 3、此类中声明单元测试方法:
* 此时的单元测试方法,方法权限是 public ,没有返回值,没有形参
* 4、此单元测试方法上需要声明注解 @Test,并在单元测试类中导入
* 5、声明好单元测试方法后,就可以在方法体类测试
* 6、写完代码后,左键双击方法名,右键 run as->JUnit Test
*
* 说明:
* 1、如果执行结果没有任何异常:绿条
* 2、如果执行结果出现异常:红条
* @author an
*
*/
public class JUnitTest {
int num = 10;
@Test
public void testEquals() {
String s1 = "MM";
String s2 = "MM";
System.out.println(s1.equals(s2));
// Object obj = new String("GG");
// Data date = (Date)obj;
}
}
包装类的使用(Wrapper)
针对八种基本数据类型定义相应的引用类型——包装类.
static 关键字
我们有时希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份。这时需要使用static关键字。
package com.an.accept;
/**
* static 关键字的使用
* 1、static:静态的
* 2、static 可以用来修饰属性,方法,代码块,内部类
* 3、使用 static 来修饰属性,静态变量(类变量)
* 属性按是否使用 static 修饰分为:静态属性,非静态属性(实例变量)
* 当修改一个对象中的非静态属性值时,不会导致其他对象中的同样属性值的修改
* 多个对象共享同一个静态变量,当通过某一个对象修改静态变量时,会导致其他对象
* 中对应的静态变量被修改过。
* 其他说明:
* ① 静态变量随着类加载而加载,可以通过"类.静态变量"的方式调用
* ② 静态变量的加载要早于对象的创建
* ③ 由于类只会加载一次,则静态变量在内存中只存在一份
* ④ 类变量 实例变量
* 类 yes no
* 对象 yes yes
*
* 静态属性距离:System.out; Math.PI;
*
* 4、使用 static 修饰方法:静态方法
* ① 随着类的加载而加载,可以通过"类.静态方法"的方式调用
* ② 不能在静态方法中调用非静态方法
* 5、static 注意点
* 在静态方法内,不能使用 this 关键字,super关键字
*
* 6、在开发中如何确定一个属性/方法是否要声明为 static?
* 属性可以被多个对象共享,不会随着对象的不同而不同
* 操作静态属性的方法通常设置为静态
* 工具类中的方法习惯上声明为静态方法,Math.Arrays,Collections
* @author an
*
*/
public class StaticTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.name = "张三";
c1.age = 40;
Chinese c2 = new Chinese();
c2.name = "李四";
c2.age = 30;
c1.nation = "CHN";
System.out.println(c2.nation);
Chinese.show();
}
}
class Chinese {
String name;
int age;
static String nation;
static public void show() {
System.out.println("中国人");
}
}
单例设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构,编程风格,以及解决问题的思考方式。
单例模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为 private,这样,就不能用 new 操作符在类的外部产生类的对象了,但是在类的内部可以产生对象,因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法来返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
package com.an.accept;
// 饿汉式
public class SingletonTest1 {
public static void main(String[] args) {
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
System.out.println(bank1 == bank2);
}
}
class Bank {
// 1、私有化类的构造器
private Bank() {
}
// 2、内部创建类的对象
// 4、要求此对象也必须声明为静态的
static private Bank instance = new Bank();
// 3、提供公共的方法,返回类的对象
static public Bank getInstance() {
return instance;
}
}
// 懒汉式
class Order1 {
// 1、私有化类的构造器
private Order1() {
}
// 2、声明当前类对象,没有初始化
private static Order instance = null;
// 3、声明 public,static 返回当前类对象的方法
public static Order getInstance() {
if(instance == null)
instance = new Order();
return instance;
}
}
饿汉式:坏处:对象加载时间过长 好处:天然线程安全
懒汉式:好处:延迟对象的创建 好处:无法保证线程安全
单例模式应用场景
由于单例模式只生成一个实例,减少了系统的开销,当一个对象产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式解决。
举例:java.lang.Runtime
网站技术器,单例模式,否则难以同步
日志应用。
数据库连接池。
读取配置文件的类,因为没必要每次使用配置文件数据,都生成一个对象去读取。
Main 方法的理解
package com.an.accept;
/**
* 1、main() 方法作为程序的入口
* 2、main() 方法也是一个普通的静态方法
* 3、main() 方法可以作为我们与控制台交互的方式
* 4、java MainTest 参数1 参数2
* @author an
*
*/
public class MainTest {
public static void main(String[] args) {
Main.main(new String[100]);
}
}
class Main {
public static void main(String[] args) {
for(int i = 0;i < args.length;i++) {
args[i] = "args_" + i;
System.out.println(args[i]);
}
}
}
代码块
package com.an.Demo;
/**
* 类的成员之四:代码块(初始化块)
*
* 1、代码块的作用,用来初始化类或对象
* 2、代码块如果有修饰的话,只能是 static
* 3、分类:静态代码块,非静态代码块
*
* 4、静态代码块
* > 内部可以有输出语句
* > 随着类的加载而执行,只执行一次
* > 作用:初始化类的信息
* 5、非静态代码块
* > 内部可以有输出语句
* > 随着对象的创建而创建
* > 每创建一个对象就执行一次非静态代码块
* > 作用: 可以在创建对象时对对象属性等初始化
* @author an
*
*/
public class BlockTest {
public static void main(String[] args) {
String desc = Person.desc;
Person p = new Person();
}
}
class Person {
// 属性
String name;
int age = 24;
static String desc = "我是一个人";
// 构造器
public Person() {
}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
// 代码块
static {
System.out.println("hello,static block");
}
{
System.out.println("hello,block");
}
// 方法
public void eat() {
System.out.println("吃饭");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
代码块比构造器先执行。
final 关键字
package com.an.Demo;
/**
* final:最终的
* 1、final 可以用来修饰的结构: 类、方法、变量
* 2、final 用来修饰一个类,此类不能被其他类继承:
* String 类,System 类,StringBuffer 类。
* 3、final 用来修饰方法:表明此方法不可以被重写,比如 Object中的getClass()
* 4、final 修饰变量,此时的变量就称为是一个常量
* 1) 修饰属性:可以考虑赋值的位置有:显式初始化,代码块中初始化,构造器中初始化。
* 2) 修饰局部变量:尤其是使用final修饰形参时,表明此形参是个常量。
* static final 用来修饰属性:全局常量
* @author an
*
*/
public class FinalTest {
}
class AA {
final int A = 20;
public final void show() {
}
}
final class FinalA {
}
抽象类和抽象方法
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用,类的设计应该保证父类和子类能够共享特征,有时将一个父类设计的非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
package com.an.know;
/**
* abstract 关键字的使用
*
* 1、abstract: 抽象的
* 2、abstract可以用来修饰的结构:类、方法
* 3、abstract 修饰类
* > 此类不能实例化
* > 抽象类中一定有构造器,便于子类对象实例化时调用
* > 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作
*
* 4、abstract 修饰方法
* > 抽象方法只有方法的声明,没有方法体
* > 包含抽象方法的类,一定是个抽象类,反之,抽象类中可以没有抽象方法
* > 子类重写了父类的所有抽象方法后,此子类方可实例化
* 若没有重写父类的所有抽象方法,子类还是抽象类
*
* 5、abstract 使用的注意点
* > abstract 不能用来修饰: 属性、构造器等结构
* > abstract 不能用来修饰私有方法、静态方法、final 的方法
* @author an
*
*/
public class AbstractTest {
// 一旦 Person 类抽象了,就不可实例化
public static void main(String[] args) {
// 创建一个匿名子类的对象
Person p = new Person() {
@Override
void walk() {
// TODO Auto-generated method stub
System.out.println("走");
}
};
}
}
abstract class Person {
String name;
int age;
// 抽象方法
abstract void walk();
public Person() {
}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃饭");
}
}
class Student extends Person {
public Student(String name,int age) {
super(name,age);
}
@Override
public void walk() {
System.out.println("走路");
}
}
模板方法设计模式
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展,改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
-
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
-
换句话说,在软件开发中实现一个算法时,整体不厚很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
接口(interface)
一方面,有时候必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java 不支持多重继承。有了接口,就可以得到多重继承的效果。
另一方面,有时必须从几个类中抽取一些共同的行为特征,而它们之间又没有 is-a 的关系,仅仅是具有相同的行为特征而已。
接口就是规范,定义的是一组规则,体现了现实世界中"如果你是...则必须能..."的思想。继承是一个"是不是"的关系,而接口实现是"能不能"的关系。
接口的本质是契约,标准,规范。
package com.an.answer;
import javax.security.auth.login.FailedLoginException;
/**
* 接口的使用
* 1、接口使用interface来定义
* 2、在java中,接口和类是并列的结构
* 3、如何定义接口,定义接口中的成员
* > JDK7 及以前
* 只能定义全局常量和抽象方法
* 全局常量 public static final,书写时可以省略
* 抽象方法:public abstract
* > JDK8
* 除了定义全局变量和抽象方法之外,还可以定义静态方法,默认方法
*
* 4、接口中不能定义构造器,不可实例化
*
* 5、Java 开发中,接口都通过让类去实现(implements)的方式来使用
* 如果实现类覆盖了接口中的所有抽象方法,则此类就可以实例化
* 如果实现类没有覆盖接口中所有抽象方法,就不可实例化
*
* 6、Java 类可以实现多个接口 -> 弥补了Java单继承的局限性
* 格式: class AA extends BB implements CC,DD,EE
*
* 7、接口与接口之间可以继承,而且可以多继承
*
* 8、接口的具体使用能够体现多态性
*
* 9、接口中实际上可以看做一种规范
* @author an
*
*/
public class InterfaceTest {
public static void main(String[] args) {
// 1、创建接口的非匿名实现类的非匿名对象
Plane plane = new Plane();
// 2、创建接口的匿名实现类的非匿名对象
Flyable plane1= new Flyable() {
@Override
public void stop() {
// TODO Auto-generated method stub
}
@Override
public void fly() {
// TODO Auto-generated method stub
}
};
}
}
interface Flyable {
// 全局常量
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;
// 抽象方法
public abstract void fly();
public abstract void stop();
}
interface Attackable {
void shot();
}
class Plane implements Flyable,Attackable {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("起飞");
}
@Override
public void stop() {
// TODO Auto-generated method stub
System.out.println("停止");
}
@Override
public void shot() {
// TODO Auto-generated method stub
}
}
interface AA {
void m1();
}
interface BB {
void m2();
}
interface CC extends AA,BB {
}
接口应用:代理模式
代理模式是为其他对象提供一种代理来控制对这个对象的访问。
package com.an.answer;
/**
* 接口的应用,代理模式
* @author an
*
*/
// 静态代理
public class NetWorkTest {
public static void main(String[] args) {
Server server = new Server();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browser();
}
}
interface NetWork {
public void browser();
}
class Server implements NetWork {
@Override
public void browser() {
// TODO Auto-generated method stub
System.out.println("真实的服务器在访问网络");
}
}
class ProxyServer implements NetWork {
private NetWork work;
public ProxyServer(NetWork work) {
this.work = work;
}
public void check() {
System.out.println("检查");
}
@Override
public void browser() {
// TODO Auto-generated method stub
check();
work.browser();
}
}
接口应用:工厂模式
工厂模式:实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
简单工厂模式:
工厂类一般使用静态方法,通过接收的参数的不同来返回不同的实例对象。
缺点:对于增加新产品,不修改代码的话,无法扩展,违反了开闭原则(对扩展开放,对修改封闭)。
工厂方法模式:
简单工厂只有一个(对一个项目或一个独立的模块而言)工厂类,工厂方法模式有一组实现了相同接口的工厂类。
抽象工厂模式:
抽象工厂模式和工厂模式的区别在于需要创建对象的复杂程度上。
给用户提供一个接口,可以创建多个产品族中的产品。
内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
在 Java 中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
package com.an.answer;
import java.text.Bidi;
import javax.imageio.event.IIOReadWarningListener;
import org.omg.CORBA.PUBLIC_MEMBER;
/**
* 内部类
* 1、Java 中允许将一个类A声明在另一个类B中,则类A就是内部类,类B为外部类
* 2、内部类分类:成员内部类(静态的、非静态的),局部内部类(方法内,代码块内,构造器内)
* 3、成员内部类
* > 一方面作为外部类的成员,可以调用外部类的结构,可以用static修饰
* > 另一方面,作为一个类,可以定义属性、方法、构造器、内部类等,可以被final修饰
* 可以被abstract修饰
* 4、关注如下问题:
* > 如何实例化成员内部类的对象
* > 如何在成员内部类中区分调用外部类的结构
* > 开发中局部内部类的使用
*
* @author an
*
*/
public class InnerClassTest {
public static void main(String[] args) {
// 创建 Dog 实例
Person.Dog dog = new Person.Dog();
dog.show();
// 创建 Bird 实例
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
}
// 返回一个实现了 Comparable 接口的对象
public Comparable getComparable() {
// 方式 1
// class MyComparable implements Comparable {
//
// @Override
// public int compareTo(Object o) {
// // TODO Auto-generated method stub
// return 0;
// }
//
// }
// return new MyComparable();
// 方式 2
return new Comparable() {
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
class Person {
// 静态成员内部类
String name;
int age;
static class Dog {
String name;
int age;
public void show() {
System.out.println("show");
}
}
//非静态成员内部类
class Bird {
String name;
public void sing() {
eat();
System.out.println("唱歌");
}
// 区分各种 name 属性
public void display(String name) {
System.out.println(name);
System.out.println(this.name);
System.out.println(Person.this.name);
}
}
public void eat() {
System.out.println("人吃饭");
}
}