Java学习笔记——从零开始(2018.04.22已更新)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39776901/article/details/79846894

2018.04.08
—-Java开发入门—-

-计算机基本概念—
•什么是计算机
计算机是一种能够按照程序运行,自动、高速处理海量数据的现代化智能电子设备。由硬件和软件所组成,没有安装任何软件的计算机称为裸机。常见的形式有台式计算机、笔记本计算机、大型计算机等:
•什么是软件
计算机软件是使用计算机过程中必不可少的东西,计算机软件可以使计算机按照事先预定好的顺序完成特定的功能
一台计算机由: 主板、CPU、内存、电源、显卡、硬盘等组成、这些是必须的,只要主机工作,这几样缺一不可
•计算机语言的发展:
1、机器语言
机器语言是直接用二进制代码指令表达的计算机语言,指令是用0和1组成的一串代码,它们有一定的位数,并分成若干段,各段的编码表示不同的含义
2、汇编语言是使用一些特殊的符号来代替机器语言的二进制码,计算机不能直接识别,需要用一种软件将汇编语言翻译成机器语言
3、高级语言
使用普通英语进行编写源代码,通过编译器将源代码翻译成计算机直接识别的机器语言,之后再由计算机执行

—-DOS常用命令—-
•dir
无参数,查看当前所在目录的文件和文件夹
/s:查看当前目录以及其所有子目录的文件和文件夹
/a:查看包括隐含文件的所有文件
/ah:只显示出隐含文件
/w:以紧凑方式(一行显示5个文件) 显示文件和文件夹
/P:以分页方式(显示一页之后会自动暂停) 显示
I more:前面那个符号是“\”上面的那个,叫做重定向符号,就是把一个命令的结果输出为另外一个命令的参数。more也是一个命令,dir /w I more得到的结果和dir /w /p的结果是一样的。其他的参数大家可以用。dir /?查看

•cd
cd 目录名: 进入特定的目录。
如果看到有个目录显示为: abcdef.ghi 就输入:cd abcdef.ghi进入该目录
cd \ 退回到根目录
cd .. 退回到上一级目录

•ma rd
md 目录名:建立特定的文件夹(dos下面习惯叫目录,win下面习惯叫文件夹)
rd 目录名:删除特定的文件夹

• cls
清除屏幕

•copy
copy 路径\文件名 路径\文件名:把一个文件拷贝到另一个地方。
注意,copy 目录 目录 只拷贝目录下的文件,不拷贝目录下的目录

•move
move 路径\文件名 路径\文件名: 把一个文件移动(就是剪切+复制) 到另一个地方。

•del
del 文件名: 删除一个文件。
del .: 删除当前文件夹下所有文件。del不能删除文件夹。

•deltree
删除文件夹和它下面的所有子文件夹,慎用

•type
type 文本文件名: 显示出文本文件的内容。

•ren
ren 旧文件名 新文件名: 改文件名。

—-java语言发展史—-
SUN公司于1995年推出
1991年Sun公司的James Gosling(java语言之父)等人开始开发名称为Oak 的语言。希望用于控制嵌入在有线电视交换盒、PDA 等的微处理器
Internet使Java成为网上最流行的编程语言,Java 对Internet 的影响也意义深远1994年将Oak 语言更名为Java
Java 既安全、可移植,又可跨平台,而且人们发现它能够解决Internet上的大型应用问题1996年获得1亿美金的投资
1998年提出JDK1.2,更名为Java2
之后推出JDK1.3,JDK1.4
2005年更名为JDK 5.0,是对以前版本最大改进
2007 年JDK6.0
2009年4月Oraclel以74亿美元收购SUN
2011年7月由Oracle正式发布Java7
2014年3月18日发布Java8正式版

—-JDK下载—-
JDK(Java Development Kit)java开发工具包
JRE(Java Runtime Environment)java运行环境
下载地址:http://www.oracle.com/technetwork/java/archive-139210.html

—-java体系与特点—-
java SE:标准版 各应用平台的基础,桌面开发和低端商务应用的解决方案
java EE:企业版 以企业为环境而开发应用程序的解决方案
java ME:微型版 致力于消费产品和嵌入式设备的最佳解决方案(基本不用了)

特点:
一种纯面向对象的编程语言
一种与平台无关(跨平台)语言
一种健壮的语言,吸收了C/C++语言的优点
有较高的安全性。(自动回收垃圾,强制类型检查,取消指针)

—-JDK安装—-
双击打开,各种下一步….

—-配置环境变量—-
在桌面右击我的电脑-属性-高级系统设置-环境变量
在path加入java安装目录下的bin目录

—-程序开发步骤—-
程序是为实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合。
第一步:编写程序 HelloWorld.java文件
第二步:编译程序 通过编译器生成 HelloWorld.class文件
第三步:运行程序 JVM(java运行平台)

—-Hello World入门—-
新建文件HelloWorld.java
右键用记事本打开
输入

public class HelloWorld{
    public static void main(String[] args){
    System.out.println("hello world!");
    }
}

ctrl+s保存
打开cmd
javac 该文件的全路径
java 该文件,去掉后缀名

—-文档注释—-
文档注释可以生成文档
eg:

/**
文档注释
public:关键字,公共的(共有的)
class:关键字,类别(分类)
HelloWorld:自定义的名字,表示一个类名
{}表示范围(作用域)
*/
public class HelloWorld{
    /*
    多行注释
    static:关键字,静态的
    void:关键字,返回值类型(空,没有返回值)
    main:表示主函数名称
    main后面的() 表示函数的参数列表
    String[]:表示字符串数组
    args 表示字符串数组的名称
    */
    public static void main(String[] args){
        //单行注释
        //向控制台输出一句话:hello world!
        //一个分号表示一条语句
        System.out.println("hello world!");
    }
}

代码注意java代码的位置

java是严格区分大小写的
java是一种自由格式的语言
代码分为结构定义语句和功能执行语句
功能执行语句的最后必须用英文分号结束

–avadoc与跨平台原理—-
cmd下运行
javadoc HelloWorld.java
会生成很多东西,双击index.html打开

跨平台原理
java源代码-编译-java字节码(跨平台)
jre可以在各个平台翻译.class文件

—-开发工具配置与反编译—-
在面向对象阶段建议用notepad++,具体如何配置在博主的另一篇文章中查看《Java——使用notepad++编译运行》https://blog.csdn.net/qq_39776901/article/details/78150581

反编译工具cavaj
下载地址:http://cavaj-java-decompiler.en.softonic.com/
下载后,解压,安装
直接将.class文件拖到cavaj里就可以反编译

—-垃圾回收器(GC)—–
不再使用的内存空间应当进行回收——垃圾回收。在C/C++等语言中,由程序员负责回收无用内存
垃圾回收器在java程序运行过程中自动启用,用于检查并释放那些可以被释放的存储空间,程序员无法精确控制和干预

—-java编程基础—-

—-变量与标识符—-
数学名词:
变数或变量,是指没有固定的值,可以改变的数。变量以非数字的符号来表达,一般用拉丁字母。变量是常数的相反。变量的用处在于能一般描述指令的方式
计算机解释:
变量就是系统为程序分配的一块内存单元,用来存储各种类型的数据。根据所存储的数据类型的不同,有各种不同类型的变量。变量名代表这块儿内存中的数据。

变量分类:
按所属的数据类型划分:
基本数据类型变量 引用数据类型变量
按被声明的位置划分:
局部变量:方法或语句块内部定义的变量
成员变量:方法外部、类的内部定义的变量

•标识符命名规则:
由字母、数字、下划线(_)和美元符号($)组成
中文也可以,但是不推荐
不能以数字开头
区分大小写
长度无限制
不能是java中的关键字和保留关键字
标识符命名习惯:驼峰命名法、见名知意。以单词或单词组合来命名

—-八种基本数据类型—-
字节(Byte)是:计算机文件大小的基本计算单位。
1字节=8位(Bit)
Bit意为“位”或“比特”,是计算机运算的基础;二进制中的一位,是二进制中的一位,是二进制最小信息单位。二进制可以用来表示一个简单的正/负的判断,有两种状态的开关(如电灯开关)
说明:
在内存计算中,字节少的数据运算速度更快;在硬盘存储中,字节少的数据类型也可以充分存入更多的数据

数据类型 关键字 占用字节 取值范围 默认值
布尔型 boolean 不定值 true,false false
字节型 byte 1 -128~127 0
短整型 short 2 -32768~32767 0
整形 int 4 0
长整型 int 8 0
字符型 char 2 0~65535 ‘\u0000’(代表一个空格)
单精度浮点型 float 4 0.0F
双精度浮点型 double 8 0.0D

注:英文一个字母占一个字节,中文一个汉字占两个字节

声明变量的格式:
(1)声明变量
类型 变量名;
(2)声明的同时给变量赋值
类型 变量名 = 值;
(3)给已声明过的变量赋值
变量名 = 值;

public class Test1{
public static void main(String[] args){
        //八种基本数据类型
        //数值类型
        byte b = 10; //占用1个字节,取值范围-128-127
        short s = 10; //占用2个字节,取值范围-32767~32768,在实际开发中使用较少
        int i = 10; //占用4各字节,
        long lon = 100L; //占用8个字节
        float f = 10.98F; //占用4个字节
        double d = 10.123456D; //占用8个字节
        //字符类型
        char c = 'A'; //占用2个字节,范围:0-65535
        //特殊
        char c2 = 97; //此时c='a' 因为ASCII码表中97对应的是a,56对应的是A
        //布尔类型
        boolean bool = true; //占用字节不确定,真实存储的时候会换算成其他类型来存储,换成整形就是4个字节,换成字节型就是1个字节

        //定义变量的格式
        //类型 变量名;//声明一个变量
        b = 20; //赋值
    }
}

—-进制与转换—-
计算机通常有:十进制、二进制、八进制、十六进制

十进制:逢十进一,人类算术使用十进制
二进制:逢二进一,即只有0、1两个值。计算机中的数据都是以二进制数字保存。
八进制:基数为八。java八进制数据要以0开头。如:0123
八进制转换成二进制:只需要将每个八进制数字替换为相对应的三个二进制位即可。
十六进制:二进制表示方法太冗长,所以在程序中一般喜欢用十六进制
十六进制:基数为十六,逢十六进一。它用abcdef表示从0-9之上的值
java中十六进制数据要以0x或0X开头。如:0X23D
十六进制转换成二进制只需将每个十六进制数字替换为相对应的四个二进制位即可

•补码

事实上,计算机内的二进制数值是以补码形式表示的
补码:一个正数的补码和其原码的形式是相同的
负数的补码是:将该数的绝对值的二进制形式,按位取反再加1
由此可知,二进制补码数值的最高位(最左位)是符号位:该位为0,表示数值为证书;该位为1,表示数值为负数
10  00000000 00000000 00000000 00001010
-10 11111111   11111111   11111111   11110110
主要原因:使用补码,可以将符号位和其他位统一处理
同时,减法也可按加法来处理(计算机只会算加法)
另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃

•二进制数转换成十进制数:
按权相加:把二进制数首先写成加权系数展开式,然后按十进制加法规则求和
•十进制数转换成二进制数:
整数部分:“除2取余,逆序排列”法
小数部分:“乘2取整,顺序排列”法

89.625
2 89         0.625
2 44...1      X2
2 22...0      1.25
2 11...0      X2
2 5 ...1      0.5
2 2 ...1      X2
2 1 ...0      1.0
0   ...1

89 1011001    0.625 0.101
89.625   1011001.101

—-基本数据类型转换—-
•自动类型转换:容量小的类型自动转换成容量大的数据类型
byte,short,int他们三者在计算时会转换成int类型
如果把int值转换为float值,或者long转换为double值,不需要强制转换,但可能丢失精度
float和double是属于近似值
byte char short int long属于精确值
精确值转近似值时可能丢失精度
如果用于表示货币值,不建议使用float或double

面试题:

short s1 = 10;
s1 = s1 + 1;
System.out.println(s1);

上面不会输出11,在第二步中,s1+1=11,此时的s1被自动转换成int类型,所以没什么错,但是这个11是int类型,而变量s1是short类型,int类型不能自动转换成short类型,所以编译报错,错误:从int转换到short可能会有损失

改为:

short s1 = 10;
int s2 = s1 + 1;
System.out.println(s2);

或者改为:

short s1 = 10;
s1 = (short)s1 + 1;
System.out.println(s2);

上题第二步,即使改为s1 = s1 + b;也会报错,道理同上

•强制类型转换
容量大的类型转换成容量小的数据类型时,要加上强制转换符。

long n = 100L;
int i = (int)n;

有可能造成精度降低或数据溢出,使用时要小心
boolean类型不能转换成任何其他数据类型

—-关键字—-

abstract  assert  boolean break  byte
case   catch  char  class  const
continue  default  do  double  else
enum  extends  final  finally  float
for   goto   if  implements import
instanceof int   interface long   native
new   package  private protected  public
return  strictfp  short static  super
switch  synchronized this  throw  throws
transient  try   void  volatile  while
保留关键字 如:goto和const

—-转义字符—-
转义字符:用于转义的符号:/

/n 换行(LF),将当前位置移到下一行开头
/r 回车(CR),将当前位置移到本行开头
/t 水平制表(HT)(跳到下一个TAB位置)
// 代表一个反斜线字符“/”
/' 代表一个单引号(撇号)字符
/" 代表一个双引号字符

注:换行和回车,在Linux系统中,直接/r就行,在Windows系统中,文件IO,在文件中写入回车就得用/n/r才能继续输入

—-运算符与优先级—-
•算术运算符(运算顺序是从左往右):

运算符
+ 加法
- 减法
* 乘法
/ 除法
% 取余(取模)
++ 自增
-- 自减

表达式:由变量、常量运算符组成的式子。
++:如果是前缀,先对比变量加1,再执行其他操作,如果是后缀,先执行其他的操作,再对此变量加1
–:与++同理,减1操作
键盘输入:
Scanner input = new Scanner(System.in);
用的时候需要在开头加入:
import java.util.Scanner; //接受键盘输入的工具类

eg:

import java.util.Scanner;
public class Test1{
    public static void main(String[] args){
        //创建一个键盘输入的工具
        Scanner input = new Scanner(System.in);
        System.out.println("请输入一个整数:");
        int x1 = input.nextInt();//从键盘接收一个整数
        System.out.println(x1);
        System.out.println("请输入一个字符串:");
        String s = input.next();//从键盘接受一个字符串;
        System.out.println(s);
    }
}

注:在jdk安装目录下,有src的压缩包,里面放的源代码,里面的./java下面是可以用的包,其中有一个包叫做lang,这个包下面的所有类默认引入,都不需要引入

int x = 1;
int y = 2;
y = x + y - (++y); //并不是括号里面的先运算,这里的括号只代表++y是一个整体

answer: x = 1,y = 0

•赋值运算符:作用是将一个值赋给一个变量,运算顺序从右到左

=  赋值
+=  加等于
-=  减等于
*=  乘等于
/=  除等于
%=  模等于

eg:

a = 1;a += 3;
answer: a = 4

之前说过的面试题:

short s = 10;
s = s + 1;
System.out.println(s);

第二步改成s += 1;就不会有问题了

•关系运算符:作用是比较两边的操作数,结果总是boolean型的

== 相等于
!= 不等于
< 小于
> 大于
<= 大于等于
>= 小于等于

2018.04.09
·逻辑运算符:用于对boolean型结果的表达式进行运算,运算结果总是boolean型

运算符 描述  
&       与
|       或
^       异或
!       非
&&      短路与
||      短路或
/**
    逻辑运算符
*/
public class Test{
    public static void main(String[] args){
        boolean a = true;
        boolean b = false;

        //与运算,分别计算表达式两边的结果再作&运算,只有两个都为true,结果才为true,否则为false
        Systeam.out.println(a&b);
        //或运算,分别计算表达式两边的结果在做|运算,只要有一个为true,结果就为true,两边都为false才为false
        System.out.println(a^b);

        //异或运算,两边相同为false,不同为true
        System.out.println(a^b);

        //非运算
        System.out.println(!b);

        //短路与
        System.out.println(a&&b);
        //短路或
        System.out.println(a||b);
    }
}
/**
通过键盘输入声明初始化一个圆柱体的高和底面半径,π值是3.14,求圆柱体的体积,并显示输出圆柱体的体积;π(r^2)h

*/
import java.util.Scanner;
public class Test{
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        System.out.println("请输入圆柱体的高:");
        int h = input.nextInt();
        System.out.println("请输入圆柱体的底面半径:");
        int r = input.nextInt();
        double result = 3.14*r*r*h;
        System.out.println("圆柱体的体积是:"+result);
    }
}
/**
    定义一个五位整数,求各个位上的数的和
*/
public class Test{
    public static void main(String[] args){
        int num = 54321;
        int w = num/10000;
        int q = num/1000%10;
        int b = num/100%10;
        int s = num/10%10;
        int g = num%10;
        System.out.println(w+q+b+s+g);
    }
}
/**
    根据天数(46)计算周数和剩余的天数,天数是通过键盘输入
*/
import java.util.Scanner;
public class Test{
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        System.out.println("请输入天数:");
        int day = input.nextInt();
        System.out.println("周数是:"+(day/7));
        System.out.println("剩余的天数:"+(day%7));
    }
}

面试题:&和&&的区别

&:在逻辑运算时:分别计算表达式两边的结果,再做&运算
在做位运算时,表示按位与
&&:短路与运算,先计算左边的表达式,如果结果是false,那么不用计算右边表达式,直接返回false
如果左边的表达式结果是true,再计算右边的表达式,如果右边的表达式为true,结果为true,否则为false

| 和 ||的原理同上
所以短路与 或 短路或的计算效率更高,建议使用。

—-位运算符与变量交换—-
·位运算符:对两个操作数中的没一个二进制位都进行运算
位运算符功能:
1、按位取反 ~ eg:~10 = -11

       10   0000 1010
      -11   1111 0101

2、按位与& 两个数只要有一个是0,结果就为0,两个数都是1,结果才为1
3、按位或| 两个数只要有一个是1,结果就为1,两个数都是0,结果才为0
4、按位异或^ 相同为0不同为1

·位移运算符:

左移:“a<<b;”:将二进制形式的a逐位左移b位,最低位空出的b位补0
带符号右移:“a>>b”:将二进制形式的a逐位右移b位,最高位空出的b位补原来的符号位
无符号右移:“a>>>b”:将二进制形式的a逐位右移b为,最高为空出的b位补0

eg:

//面试题:以最快的速度计算8*4的结果
public class Test{
    public static void main(String[] args){
        //int result = 8 * 4; wrong
        //0000 1000 8
        //0010 0000 32
        System.out.println(8<<2);//最快的计算速度
    }
}

使用位移的方法是最快的
左移算法:位移数作为2的次幂与操作数相乘
10<<2=40
右移算法:操作数除以位移数的2次幂
40>>2=10

//面试题:通过键盘输入,初始化两个数将这两个数据交换位置后,输出显示交换后的数据
import java.util.Scanner;
public class Test{
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        System.out.println("请输入第一个数:");
        int a = input.nextInt();
        System.out.println("请输入第二个数:");
        int b = input.nextInt();

        //(1)通过第三个变量来交换
        // int c = a;
        // a = b;
        // b = c;
        // System.out.println("a="+a+"b="+b);

        //(2)不通过第三个变量如何交换两个数?

        //a.通过表达式
        // a = a+b-(b=a);
        // System.out.println("a="+a+"b="+b);
        //b.通过+-计算,*/也可以(但有可能会溢出)
        // a = a + b;
        // b = a - b;   
        // a = a - b;
        // System.out.println("a="+a+"b="+b);
        //c.通过异或,最快的方法,不能用两个相同的值
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
        System.out.println("a="+a+"b="+b);
    }
}

//异或方法详解
import java.util.Scanner;
public class Test{
public static void main(String[] args){
int a = 1; //a = 0000 0001
int b = 2; //b = 0000 0010

    a = a ^ b;  //a = 0000 0011
    b = a ^ b;  //b = 0000 0001
    a = a ^ b;  //a = 0000 0010
    System.out.println("a="+a+"b="+b);
}

}

—-三目运算符与优先级—-
·三目运算符:X?Y:Z
Z为boolean类型的表达式,先计算x的值,若为true,整个三目运算的结果为y的值,否则整个运算结果为z的值

eg:

//从键盘输入一个成绩,判断该分数是否及格
import java.util.Scanner;
public class Test{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        System.out.println("请输入成绩:");
        int score = in.nextInt();
        boolean bool = score>=60?true:false;
        System.out.println(bool);
    }
}

·优先级

1   () []
2   ! +(正) -(负) ~ ++ --
3   * / %
4   +(加) -(减)
5   << >> >>>
6   < <= > >= instanceof
7   == !=
8   &(按位与)
9   ^
10  !
11  &&
12  ||
13  ?:
14  = += -= *= /= %= &= != ^= ~= <<= >>= >>>=

—-分支语句—-

if(表达式){
    执行语句;
}else{
    执行语句;
}
//写一个程序,判断某一年是否为闰年(被4整除但不能被100整除,能被400整除是闰年,否则是平年)
import java.util.Scanner;
public class Test{
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        System.out.println("请输入年份");
        int year = input.nextInt();
        if((year%4==0 && year%100!=0) || year%400==0){
            System.out.println("是闰年");
        }else{
            System.out.println("平年");
        }
    }
}

·分支嵌套

if(表达式1){
    执行语句;
}else if(表达式2){
    执行语句;
}else{//可有可无
    执行语句;
}
switch(表达式){
    case 取值1:语句块1;
    break;
    case 取值2:语句块n;
    break;
    default:语句块n+1;
    break;
}

·switch语句有关规则
1、表达式的返回值必须是下述几种类型之一:int,byte,char,char,short,确定的结果,jdk1.7后支持String;
2、case子句中的取值必须是常量,且所有case子句中的取值应是不同的;
3、default子句是可选的;
4、break语句用来执行完一个case分支后使程序跳出switch语句块;如果case后面没有写break则直接往下面执行;
5、case后面的执行体可写{}也可以不写{}
6、在实现分支判断时,一个好的习惯是把匹配成功率高的pandaun放在前面,可以提高效率
7、在表达式中判断,我们尽量使用确定的值去跟不确定的值去判断,eg:if(10==x)

·if与switch比较
同:都是选择分支语句
不同:
1、语法不同
2、语句后面表达式值的类型不同,if语句表达式值的类型是boolean,switch分支表达式的值的类型是byte,short,char,int,枚举,String。
3、适用场景
if:变量的值在某个区间之内
switch:变量的值是某个定值

·if与三目运算符
三目运算符都可以使用if语句实现,反之不成立

java7 switch支持String的实现原理:
这个新特性是在编译器这个层次上实现的
在编译的过程中,编译器会根据源代码的含义进行转换,不同的java编译器可能采用不同的方式来转换,比如:如果switch语句中只包含一个case语句,那么就可以简单的将其转换成一个if语句,如果包含一个case和一个default语句,就可以转换成if-else语句。而对于复杂的情况(多个case语句),也可以转换成java7之前的switch语句,只不过使用字符串的哈希值(整数)作为switch语句的表达式

—-循环语句—-
while
do while
for

while(条件表达式){
    //语句块
}
do{
    //语句块
}while(循环条件);
for(初始化参数;判断条件;更新循环变量){
    循环体;
}
//关键字:continue表示跳过当次循环,继续下次循环。

For循环的六种写法:
1、标准写法
2、表达式1省略,但在外部声明
3、表达式2省略,死循环
4、表达式3省略,但是要声明在循环内部
5、表达式1、3省略,但是要声明在循环的外部和内部
6、三个表达式都省略,死循环

eg:

/**
    for循环
    输出十个斐波那契数列:1 1 2 3 5 8 13 21 34 55
    (特点:第1、2两个数值为1,从第三个数开始,该数是其前面两个数之和)
*/

public class Test{
    public static void main(String[] args){
        int n1 = 1;
        int n2 = 1;
        int next = 0;
        System.out.print(n1+" "+n2+" ");
        for(int i=0;i<8;i++){
            next = n1 + n2;
            System.out.print(next+" ");
            n1 = n2;
            n2 = next;
        }
    }
}

—-方法与数组—-

—-方法的定义—-
方法(又叫函数)就是一段特定功能的代码块儿。方法提高程序的复用性和可读性

·格式
语法:

访问权限修饰符 [其他的修饰符 如static] 返回值类型 方法名(参数类型1 形参1,参数类型2 形参2,....){//形参列表
//方法体
return 返回值;
}

方法的格式说明:
修饰符:目前就用public static
返回值类型:就是功能结果的数据类型
方法名:见名知意,首字母小写,遵守驼峰命名法,方便我们的调用

在调用方法中,如果我们定义的方法有参数,就必须在调用方法的同时传入这个值,即给当前方法中的参数复制,而这个传入的值我们称之为实际参数,也就是实参

注意:
1、若当前方法中不需要使用形参,那么形参列表可以为空
2、实参和形参的类型要相互兼容,且:实参的取值范围要小于或者等于形参类型的取之范围

·方法的返回值
return:结束方法的
返回值:就是功能的结果,由return带给调用者

注意:
1、若当前方法没有返回值类型,即返回值类型是void,那么当前方法中可以不写return
2、return即表示结束一个方法,也可以将返回值返回给调用当前方法的调用者
3、return返回值时一次只能返回一个值,不可以返回多个值
4、一个方法中可以有多个return,但被执行的只能有一个,所以需要判断

·方法的重载
在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数;
返回值不能作为重载的条件,只改形参的名字也不算重载

—-数组的定义与四种赋值方式—-
·数组
当我们有一组相同类型的数据需要存储,如果此时使用单个变量来存储,我们将要定义若干个变量名,这样将会非常繁琐,并不利于维护

·四种赋值方式
一、使用默认的初始值来初始化数组中的每一个元素
语法:数组元素类型[] 数组名 = new 数组元素类型[数组中元素的个数(数组的长度)];
如:int[] scores = new int[3];

二、先声明,然后再赋予默认的初始值
语法:数组元素类型[] 数组名;
数组名 = new 数组元素类型[数组中元素的个数(数组的长度)];
如:

int[] scores;
scores = new int[3];

三、先声明,然后再使用特定的值进行初始化。
语法:数组元素类型[] 数组名 = new 数组元素类型;
如:

 int[] scores = new int[](56,78,98);

四、将第三种写法可以简化为(使用数组常量值给数组进行赋值)
语法:数组元素类型[] 数组名 = {元素1,元素2,…..}
如:

  int[] scores = {56,78,89};

注:数组一定要有长度
所有的数组都有一个属性:length
System.out.println(“数组的长度是:”+scores.length);

·数组的遍历
遍历:依次取出数组中的每一个元素
遍历方式一:普通的for循环
语法:

for(int i=0;i<数组的长度;i++){
//i:循环变量,同样也是数组的下标
数组中元素的类型 变量 = 数组名[i];
}
public class Test{
    public static void main(String[] args){
        int[] scores = {45,98,55,46,23};

        //for遍历
        int len = scores.length; //当数组的长度不变的时候,这样写的效率高
        for(int i=0;i<scores.length;i++){
            int score = scores[i];
        }

        //foreach jdk1.5之后新增的特性 增强for循环
        for(int x:scores){
            System.out.println(x);
        }
    }
    public static void print1(int[] x){
        int len = x.length;
        for(int i=0;i<len;i++){
            System.out.println(x[i]);
        }
    }

    //jdk1.5可变参数
    //可变参数只能是参数列表的最后一个
    //可变参数作为数组使用
    public static void print2(int...x){
        int len = x.length;
        for(int i=0;i<len;i++){
            System.out.println(x[i]);
        }
    }
}

·数组异常

public class Test{
    public static void main(String[] args){
        int[] scores = {45,98,55,46,23};
        //print1(null);
        print2(scores);
    }
    public static void print1(int[] x){
        //java.lang.NullPointerException(空指针异常)
        //当一个变量为null(没有赋值)时,我们去调用了该变量的属性和方法
        System.out.println("数组的长度为:"+x.length);
    }

    public static void print2(int[] x){
        //java.lang.ArrayIndexOutOfBoundsException(数组下标越界异常)
        for(int i=0;i<=x.length;i++){
            System.out.println(x[i]);
        }
    }
}

·内存分析
栈内存:大小固定,用于存储局部,临时变量(基本数据类型)和引用变量
堆内存:大小不固定:对象

//猜数游戏
import java.util.Scanner;
import java.util.Random;
public class Test{
    public static void main(String[] args){
        int[] nums = new int[5];
        int len = nums.length;
        Random r = new Random();//创建一个可以生成随机数的工具
        for(int i=0;i<len;i++){
            nums[i] = r.nextInt(50);
        }

        Scanner input = new Scanner(System.in);
        System.out.println("请输入你要猜的数:(50以内)");
        int userNum = input.nextInt();

        boolean flag = false;
        for(int x:nums){
            if(userNum==x){
                flag = true;
                break;
            }
        }

        if(flag){
            System.out.println("恭喜你,猜对了!");
        }else{
            System.out.println("没猜对,加油!");
        }
    }
}

—-二维数组与最大最小值算法—-
java中没有真正的多维数组,多维数组的表示方式是数组中的元素还是数组

//二维数组示例
public class Test{
    public static void main(String[] args){
        int[][] scores = {{1,2,3},{4,5,6},{7,8,9}};
        int classLen = scores.length;
        for(int i=0;i<classLen;i++){
            int sum = 0;
            int count = scores[i].length;
            for(int j=0;j<count;j++){
                sum+=scores[i][j];
            }
            int avg = sum/count;
            System.out.println("第"+(i+1)+"班的平均成绩是:"+avg);
        }
    }
}
//求最大值和最小值算法
public class Test{
    public static void main(String[] args){
        int[] num = {12,3,53,45,7,34,7};
        System.out.println("数列中的最大值是:"+max(num));
        System.out.println("数列中的最小值是:"+min(num));

    }
    //求数列中的最大值
    public static int max(int[] num){
        int max = num[0];
        int len = num.length;
        for(int i=1;i<len;i++){
            if(num[i]>max){
                num[i] = num[i]+max;
                max = num[i]-max;
                num[i] = num[i]-max;
            }
        }
        return max;
    }
    //求数列中的最小值
    public static int min(int[] num){
        int min = num[0];
        int len = num.length;
        for(int i=1;i<len;i++){
            if(num[i]<min){
                num[i] = num[i]+min;
                min = num[i]-min;
                num[i] = num[i]-min;
            }
        }
        return min;
    }
}

·冒泡排序算法
比较相邻的元素,如果第一个比第二个大,就交换它们两个
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数
针对所有的元素重复以上的步骤,除了最后一个
持续每次对越来越少的元素重复上面的步骤,知道没有任何一对数字需要比较
相同元素的前后顺序并没有改变,所以冒泡排序市一中稳定排序算法

//冒泡排序
public class Test{
    public static void main(String[] args){
        int[] nums = {34,4,56,56,17,90,65};//带排序的数列
        bubbleSort(nums);
        for(int x:nums){
            System.out.print(x+" ");
        }
    }
    public static void bubbleSort(int[] nums){
        //外循环控制轮数
        for(int i=0;i<nums.length-1;i++){//比较的轮数等于数列的长度-1
            //内循环控制每一轮比较的次数
            for(int j=0;j<nums.length-1-i;j++){
                if(nums[j]>nums[j+1]){
                    nums[j] = nums[j]+nums[j+1];
                    nums[j+1] = nums[j]-nums[j+1]; 
                    nums[j] = nums[j]-nums[j+1];
                }
            }
        }
    }
}

2018.04.10
·选择排序算法
每一躺从带排序的数据元素中选出最小(或最大)的一个元素,顺序放在已经排好序的数列的最后,知道全部待排序的数据元素排完。选择排序比冒泡排序的效率要高一些,是不稳定的排序方法

//选择排序算法
public class Test{
    public static void main(String[] args){
        int[] nums = {34,4,56,17,90,65};//待排序的数列
        selectSort(nums);
        for(int x:nums){
            System.out.print(x+" ");
        }
    }
    public static void selectSort(int[] nums){
        int minIndex = 0;//用于记录每次比较的最小值下标
        //外循环控制轮数
        for(int i=0;i<nums.length-1;i++){
            minIndex = i;//每轮假设一个最小值下标
            for(int j=i+1;j<nums.length;j++){
                if(nums[minIndex]>nums[j]){
                    minIndex = j;
                }
            }
            //判断需要交换的数下标是否是自己,自己就不用换了
            if(minIndex!=i){
                nums[minIndex] = nums[minIndex]+nums[i];
                nums[i] = nums[minIndex] - nums[i];
                nums[minIndex] = nums[minIndex] - nums[i];
            }
        }
    }
}

·直接插入排序算法
从后向前找到合适位置后插入
基本思想:每步将一个带排序的记录,按其顺序码大小插入到前面已经排序的子序列的合适位置(从后向前找到合适位置后),直到全部插入位置

//直接插入排序
public class Test{
    public static void main(String[] args){
        int[] nums = {34,4,56,17,90,65};//待排序的数列
        insertSort(nums);
        for(int x:nums){
            System.out.print(x+" ");
        }
    }
    public static void insertSort(int[] nums){
        for(int i=1;i<nums.length-1;i++){
            int temp = nums[i];
            int j = 0;
            for(j=i-1;j>=0;j--){
                if(nums[j]>temp){
                    nums[j+1] = nums[j];
                }else{
                    break;
                }
            }
            if(nums[j+1]!=temp){
                nums[j+1] = temp;
            }
        }
    }
}

·二分查找算法
前提是在已经排好序的数组中,通过将待查找的元素与中间索引值对应的元素进行比较,若大于中间索引值对应的元素,去右半部分查找,否则,去左半部分查找。以此类推,直到找到为止;找不到返回一个负数

//二分查找算法
public class Test{
    public static void main(String[] args){
        //必须保证数列是有序的
        int[] num = {10,20,50,65,88,90};
        int index = binarySearch(num,88);
        System.out.println(index);
    }

    public static int binarySearch(int[] num,int key){
        int start = 0;//开始下标
        int end = num.length-1;//结束下标

        while(start<=end){
            int middle = (start+end)/2; //>>>1
            if(num[middle]>key){
                end = middle-1;
            }else if(num[middle]<key){
                start = middle+1;
            }else{
                return middle;
            }
        }
        return -1;
    }
}

·Array工具类:用来操作数组(比如排序和搜索)的各种方法

//常用方法:
//使用二分法查找
Arrays.binarySearch(int[] array,int value);
//数组内容转成字符串的形式输出
Arrays.toString(int[] array);
//数组排序
Arrays.sort(int[] array);
//复制制定的数组
Arrays.copyOf(int[] array,int length);
Arrays.copyOf(int[] array,int from,int to);
System.arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
//判断两个数组是否相等
Arrays.equals();
//使用指定元素填充数组
Arrays.fill();
//Arrays工具类的使用
import java.util.Arrays;
public class Test{
    public static void main(String[] args){
        int[] num = {45,65,76,87,98,101};

        //二分查找
        int index = Arrays.binarySearch(num,98);
        System.out.println("找到的下标是:"+index);

        //返回制定数组内容的字符串表示形式
        //在测试输出数据时可以使用
        System.out.println(Arrays.toString(num));

        //排序
        int[] num2 = {10,32,11,44,543,22,12};
        Arrays.sort(num2);//快速排序
        System.out.println(Arrays.toString(num2));

        //数组的复制
        int[] num3 = Arrays.copyOf(num2,10);
        System.out.println(Arrays.toString(num3));

        //System.arraycopy
        int[] newNum = new int[num2.length];
        System.arraycopy(num2,0,newNum,0,num2.length);
        System.out.println(Arrays.toString(newNum));

        //小结,数组的复制
        /*
            效率由高到低排序是:System.arraycopy -> Arrays.copyOf -> for
        */

        //判断两个数组的值是否相等
        System.out.println(Arrays.equals(num2,newNum));

        //填充数组
        Arrays.fill(newNum,0);
        System.out.println(Arrays.toString(newNum));
    }
}

·双色球模拟综合案例
玩法说明:
双色球投注区分为红球号码区和篮球号码区,红球号码范围为01~33,蓝球号码范围为01~16,双色球每期从33个红球中开出6个号码,从16个蓝球中开出1个号码作为中奖号码,双色球玩法即是竞猜开奖号码的6个红球号码和1个篮球号码,顺序不限。

二、案例分析:
1、如何产生篮球和红球?
2、如何接收用户选号?
3、如果验证是否中奖?
4、公布本期中奖号码?

三、实现步骤
1、整体实现思路
2、随机取值不重复算法(系统和用户)
3、判断是否中奖的逻辑
4、结果输出

/**
    模拟双色球综合案例
    1、用户选择是机选还是手选号码
    2、接收用户选号(6红,1蓝)
    3、生成系统号码(6红,1蓝)
    4、比较系统号码和用户号码,记录个数
    5、验证是否中奖
    6、系统号码排序
    7、公布结果
*/
import java.util.Arrays;
import java.util.Scanner;
import java.util.Random;
public class Test{
    public static void main(String[] args){
        //定义相关的变量
        int[] userRedBall = new int[6]; //用户选择的红球号码
        int[] sysRedBall = new int[6]; //系统生成的红球号码
        int userBlueBall = 0; //用户选择的蓝球
        int sysBlueBall = 0; //系统生成的蓝球
        int redCount = 0; //记录用户选择正确的红球数
        int blueCount = 0;//记录用户选择正确的蓝球数
        int[] redBall = new int[33]; //用于存储1-33的红球号码
        //需要随机生成6个在1~33之间不重复的数(算法)
        for(int i=0;i<redBall.length;i++){
            redBall[i] = i+1;
        }

        //游戏开始,系统提示
        System.out.println("双色球游戏开始,good luck!");
        System.out.println("请问您是要机选或手选号码(1:机选,2:手选)");

        Scanner in = new Scanner(System.in);
        Random r = new Random();
        boolean flag = true;
        while(flag){
            int isAuto = in.nextInt();
            switch(isAuto){
                case 1:
                    //机选
                    computerSelection(redBall,userRedBall);//机选红球
                    userBlueBall = r.nextInt(16)+1; //机选篮球
                    flag = false;
                    break;
                case 2:
                    //手选
                    System.out.println("请选择6个红球号码(1-33):");
                    for(int i=0;i<userRedBall.length;i++){
                        userRedBall[i] = in.nextInt();
                    }
                    System.out.println("请选择一个蓝球号码(1-16)");
                    userBlueBall = in.nextInt();
                    flag = false;
                    break;
                default:
                    System.out.println("请问您是要机选或手选号码(1:机选,2:手选)");
                    break;
            }
        }

        //系统随机生成号码
        //红球
        computerSelection(redBall,sysRedBall);
        //蓝球
        sysBlueBall = r.nextInt(16)+1;

        //统计结果:
        //统计红球
        for(int i=0;i<userRedBall.length;i++){
            for(int j=0;j<sysRedBall.length-redCount;j++){
                if(userRedBall[i]==sysRedBall[j]){
                    int temp = sysRedBall[j];
                    sysRedBall[j] = sysRedBall[sysRedBall.length-1-redCount];
                    sysRedBall[sysRedBall.length-1-redCount] = temp;
                    redCount++;
                    break;
                }
            }
        }
        //统计蓝球
        if(userBlueBall==sysBlueBall){
            blueCount = 1;
        }

        //验证是否中奖
        if(blueCount==0 && redCount<=3){
            System.out.println("革命尚未成功通知还需努力!");
        }else if(blueCount==1 && redCount<3){
            System.out.println("中了六等奖,5块钱!");
        }else if((blueCount==1 && redCount==3) || (blueCount==0 && redCount==4)){
            System.out.println("中了五等奖,10块钱!");
        }else if((blueCount==1 && redCount==4) || (blueCount==0 && redCount==5)){
            System.out.println("中了四等奖,200块钱!");
        }else if(blueCount==1 && redCount==5){
            System.out.println("中了四等奖,3000块钱!");
        }else if(blueCount==0 && redCount==6){
            System.out.println("中了二等奖,150w!");
        }else if(blueCount==1 && redCount==6){
            System.out.println("中了四等奖,500w!");
        }else{
            System.out.println("系统有误,中奖无效!");
        }

        //公布系统号码
        System.out.println("本期中奖红球号码为:");
        sort(sysRedBall);
        System.out.println(Arrays.toString(sysRedBall));
        System.out.println("本期中奖蓝球号码为:"+sysBlueBall);

        //公布用户号码
        System.out.println("你选择的红球号码为:");
        sort(userRedBall);
        System.out.println(Arrays.toString(userRedBall));
        System.out.println("您选择的蓝球号码为:"+userBlueBall);

        System.out.println("买双色球,造福你我他!谢谢");

    }



    //用于在指定数列中,随机生成不重复的数算法
    public static void computerSelection(int[] ballCount,int[] redBall){
        Random r = new Random();
        int index = -1;
        for(int i=0;i<redBall.length;i++){
            index = r.nextInt(ballCount.length-i);
            redBall[i] = ballCount[index];

            int temp = ballCount[index];
            ballCount[index] = ballCount[ballCount.length-1-i];
            ballCount[ballCount.length-1-i] = temp;
        }
    }

    //冒泡排序
    public static void sort(int[] ball){
        for(int i=0;i<ball.length-1;i++){
            for(int j=0;j<ball.length-1-i;j++){
                if(ball[j]>ball[j+1]){
                    ball[j] = ball[j]+ball[j+1];
                    ball[j+1] = ball[j]-ball[j+1];
                    ball[j] = ball[j]-ball[j+1];
                }
            }
        }
    }
}

2018.4.11
面向过程:以步骤为单位,一步一步完成某一个具体事情
面向对象:以对象为单位,通过调度组合不同的对象来完成某一个事情

面向对象的三大特征:封装,继承,多态

在java中可以使用一下的语句定义一个类

class 类名称{
    属性名称;
    返回值类型 方法名称(){}
}

对象的定义:
一个类要想真正的进行操作,则必须依靠对象,对象的定义格式如下:

类名称 对象名称 = new 类名称();

如果想要访问类中的属性或方法(方法的定义)

访问类中的属性:对象.属性;
访问类中的方法:对象.方法;

在java中对象声明有两种含义
声明对象:Horse horse = null;
//表示声明了一个对象,但是此对象无法使用,horse没有具体的内存指向

实例化对象:horse = new Horse();
//表示实例化了对象,可以使用

//通过对象调用方法:

horse.eat();

//匿名对象调用方法:

new Horse().eat();

eg:

/**
    new关键字表示创建一个对象
    new关键字表示实例化对象
    new关键字表示申请内存空间

    如果使用一个没有申请内存空间的对象,会报空指针异常
    java.lang.NullPointerException
*/
public class Test{
    public static void main(String[] args){
        Horse h = null; //声明一个类的变量(除了八种基本数据类型以外,都是引用数据类型,包括数组)
        //创建一个horse类型的对象,实例化对象
        h = new Horse();
        //有了对象,我们就可以调用对象的属性和方法
        h.name = "赤兔马";
        h.age = 350;

        h.run();//调用方法,那么方法就会被执行
        h.eat();

        //匿名对象:只能使用一次,用完后该对象就会被释放
        new Horse().eat();

        h = null;//把对象释放
        //h.eat();//当对象不存在时,调用该对象的属性和方法将报错(空指针)

        //------------
        Horse h1 = null;
        Horse h2 = null;
        h1 = new Horse();
        //h2 = new Horse();
        h1.name = "小白";
        h1.age = 4;
        //h2.name = "小黑";
        //h2.age = 5;

        h2 = h1;//对象之间的赋值,相同类型才可以赋值,此时h1和h2指向了同一个对象
        h2.name = "小黑";
        System.out.println(h1.name);//输出小黑


        //------------
        Horse h3 = null;
        Horse h4 = null;
        h3 = new Horse();
        h4 = new Horse();
        h3.name = "小白";
        h3.age = 4;
        h4.name = "小黑";
        h4.age = 5;
        h3 = h4;//此时h4和h3指向堆中相同的内存空间,h4之前指向的内存空间会变成垃圾被处理
        //当一个堆中内存的对象,没有被任何一个栈中的任何引用所指向,则堆中内存的对象会被认为是垃圾
        h4.name = "嘿嘿";
        System.out.println(h3.name);//嘿嘿


    }
}

//定义一个类(类型)
class Horse{
    //在类中定义属性(特征)
    String name;
    int age;
    public void run(){
        System.out.println("我是"+name+"我"+age+"岁了,我还日行千里");
    }
    public void eat(){
        System.out.println("我吃仙草,还吃的多");
    }
}

string在32位系统中占4个字节,在64位系统中占8个字节

·小结
(1)new关键字:表示向内存申请空间,也表示实例化一个对象,创建一个对象
(2)一个对象在内存中的大小,由该对象的所有属性所占的内存大小的总和,引用类型变量在32位系统上占4个字节,在64位系统上占8个字节。加上额外的对象隐性数据所占的大小
(3)相同的类型才可以赋值
(4)不同的引用,指向同一个对象,任何一个引用改变对象的值,其它引用都会反映出来
(5)编程时要注意的问题,在确定不使用对象时,要尽早释放对象:引用=null
(6)当一个堆中的对象没有被任何引用变量所指向时,该对性爱那个会被jvm的GC程序认为是垃圾对象,从而被回收

·封装性
一、概念
1、封装性是面向对象思想的三大特征之一
2、封装就是隐藏实现细节,仅对外提供访问接口
封装有:属性的封装、类的封装、组件的封装、模块化封装、系统级封装…

二、封装的好处
1、模块化
2、信息隐藏
3、代码重用
4、插件化易于调试
5、具有安全性

封装缺点:
会影响执行效率

eg:

/**
    封装性
    如果属性没有封装,那么在本类之外创建对象后,可以直接访问属性private关键字:访问权限修饰符,public表示公有的,private表示私有的,私有的属性或方法,只能在本类中访问,公有的属性和方法,可以被类外部的其它类访问,想要在类外访问私有属性,我们需要提供公有的方法来间接访问通常在一个类中,属性都私有化,并对外提供getter and setter方法
*/

public class Test{
    public static void main(String[] args){
        Person p1 = new Person();
        //p1.name = "菲菲";
        //p1.age = 18;
        p1.setName("菲菲");
        p1.setAge(18);
    }
}

class Person{
    //属性的封装
    private String name;
    private int age;

    //getter and setter

    //对外提供一个为name属性设值的方法
    public void setName(String name){
        this.name = name;//this.name表示当前对象的name
    }
    //对外提供一个获取name属性的方法
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public int getAge(){
        return age;
    }
}

·成员变量和局部变量
1、在类中的位置不同
成员变量:在类中定义
局部变量:在方法中定义或者方法的参数
2、在内存中的位置不同
成员变量:在堆内存(成员变量属于对象,对象进堆内存)
局部变量:在栈内存(局部变量属于方法,方法进栈内存)
3、声明周期不同
成员变量:随着对象的创建而存在,随着对象的销毁而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
4、初始化值不同
成员变量:有默认初始化值,引用类型默认为null
局部变量:没有默认初始化值,必须定义,赋值,然后才能使用
注意:
局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则

·构造方法
什么是构造方法
1、构造方法就是类构造对象时调用的方法,由于对象的初始化工作
2、构造方法是实例化一个类的对象时,也就是new的时候,最先调用的方法

构造方法的定义:
构造方法是在类中定义的,构造方法的定义格式:方法名称与类名称相同,无返回值类型的声明。
对象的实例化语法:
Dog dog = new Dog();//new Dog后面有个括号,带括号表示调用了方法,此时调用的方法就是构造方法了

eg:

public class Test{
    public static void main(String[] args){
        Dog dog1 = new Dog();
        Dog dog2 = new Dog("wangwang");
        Dog dog3 = new Dog("wangwang",5);
    }
}

class Dog{
    //默认构造方法
    public Dog(){
        System.out.println("构造方法执行了");
    }
    public Dog(String name){
        this.name = name;
        System.out.println("带一个参数的构造方法执行了");
    }
    public Dog(String name,int age){
        this(name);//调用其它构造方法,此语句必须放在第一句,在构造方法相互调用时必须要有出口
        //this.name = name;
        this.age = age;
        System.out.println("带两个参数的构造方法执行了");
    }

    private String name;
    private int age;
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public int getAge(){
        return age;
    }
}

·构造方法小结
(1)构造方法名称与类名相同,没有返回值声明(包括void)
(2)构造方法用于初始化数据(属性)
(3)每一个类中都会有一个默认的无参的构造方法
(4)如果类中有显示的构造方法,那么默认构造方法将无效
(5)如果有显示的构造方法,还想保留默认构造方法,需要显示的写出来
(6)构造方法可以有多个,但参数不一样,称为构造方法的重载
(7)在构造方法中调用另一个构造方法,使用this(…),该代码必须在第一句
(8)构造方法之间的调用,必须要有出口
(9)给对象初始化数据可以使用构造方法或setter方法,通常情况下,两者都会保留
(10)一个好的编程习惯是保留默认的构造方法(为了方便一些框架代码使用反射来创建对象)
(11)private Dog(){},构造方法私有化,当我们的需求是为了保证该类只有一个对象时。
什么时候一个类只需要一个对象?比如,工具类(没有属性的类,只有行为),权衡只用一个对象与产生多个对象的内存使用,来确定该类是否要定义为只需要一个对象

·this关键字
在java基础中,this关键字是一个非常重要的概念。使用this关键字可以完成一下的操作:
调用类中的属性
调用类中的方法或构造方法
表示当前对象

·值传递与引用传递
//示例一:值传递

public class Test{
    public static void main(String[] args){
        int x = 10;
        method(x);
        System.out.println("x="+x);//10
    }
    public static void method(int mx){
        mx = 20;
    }
}

//示例二:引用传递

public class Test{
    public static void main(String[] args){
        Duck d = new Duck();
        method(d);
        System.out.println("Duck age = "+d.age);//5
    }
    public static void method(Duck duck){
        duck.age = 5;
    }
}

class Duck{
    int age = 2; //省略封装
}

//示例三:String传递

public class Test{
    public static void main(String[] args){
        String name = "小飞";
        method(name);
        System.out.println("name="+name);//小飞
    }
    public static void method(String sname){
        sname = "小贝";//一个字符串就表示一个对象,这里sname引用的指向改到小贝上面,并没有修改了小飞
    }
}

//示例四:String传递

public class Test{
    public static void main(String[] args){
        Person p = new Person();
        method(p);
        System.out.println("person name="+p.name);//贝贝
    }
    public static void method(String per){
        per.name = "贝贝";
    }
}
class Person{
    String name = "飞飞"; //省略封装
}

·对象的一对一关系

/**
    两个对象之间的一对一关系:把另一个类型作为类型的属性
    比如:
    一个英雄(Hero)对一个兵器(Weapon)代码如何表示
    双向一对一
    单向一对一
*/
public class Test{
    public static void main(String[] args){
        Hero hero = new Hero("刘备",300);
        Weapon weapon = new Weapon("双股剑",3);

        //把两个对象关联起来
        hero.setWeapon(weapon);
        weapon.setHero(hero);

        //通过英雄来获取他的信息
        String name = hero.getName();
        int age = hero.getAge();
        Weapon w = hero.getWeapon();
        System.out.println("我是"+name+"我"+age+"岁,我的武器是:"+w.getName()+",排行"+w.getGrade()+"级");
    }
}

class Hero{
    private String name;
    private int age;
    private Weapon weapon;//一对一关系
    public void setWeapon(Weapon weapon){
        this.weapon = weapon;
    }
    public Weapon getWeapon(){
        return weapon;
    }
    public Hero(){}
    public Hero(String name,int age){
        this.name = name;
        this.age = age;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public int getAge(){
        return age;
    }
}

class Weapon{
    private String name;
    private int grade;
    private Hero hero;
    public void setHero(Hero hero){
        this.hero = hero;
    }
    public Hero getHero(){
        return hero;
    }

    public Weapon(){}
    public Weapon(String name,int grade){
        this.name = name;
        this.grade = grade;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.grade = grade;
    }
    public int getGrade(){
        return grade;
    }
}

·static关键字
static关键字的作用
1、使用static关键字修饰一个属性
声明为static的变量实质上就是全局变量
2、使用static关键字修饰一个方法
通常,在一个类中定义一个方法为static,那就是说,无需本类的对象即可调用此方法
3、使用static关键字修饰一个类(内部类)

静态方法区:存储静态变量,类信息(方法)

/**
    static关键字
    1、静态变量或方法单独存储在静态方法区,不属于对象,依赖类
    2、静态变量是全局变量,生命周期从类被加载后一直到程序结束
    3、静态变量只有一份,在静态方法区中存储
    4、静态变量是本类所有对象共享一份
    5、建议不要使用对象名去调用静态数据,直接使用类名调用
    6、static修饰一个方法,那么该方法是属于类的,不属于对象,直接用类名调用
    7、静态方法不能访问非静态属性和方法,只能访问静态
*/
public class Test{
    public static void main(String[] args){
        // Role beibei = new Role("刘备","蜀国");
        // Role yunchang = new Role("云长","蜀国");
        // Role feifei = new Role("张飞","蜀国");

        Role beibei = new Role("刘备");
        Role yunchang = new Role("云长");
        Role feifei = new Role("张飞");

        System.out.println(beibei.getInfo());
        System.out.println(yunchang.getInfo());
        System.out.println(feifei.getInfo());

        System.out.println(beibei.country);
        System.out.println(yunchang.country);
        System.out.println(feifei.country);

        System.out.println("---------------------");
        beibei.country = "秦国";
        System.out.println(beibei.country);
        System.out.println(yunchang.country);
        System.out.println(feifei.country);

        System.out.println("---------------------");
        System.out.println(Role.country);//用类名调用静态变量
    }
}

class Role{
    private String name;
    static String country = "蜀国";//静态变量(全局变量)
    public Role(){}
    public Role(String name,String country){
        this.name = name;
        this.country = country;
    }
    public Role(String name){
        this.name = name;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    //静态方法不能访问非静态的数据
    public static void setCountry(String country){
        Role.country = country;
    }
    /*
    public void setCountry(String country){
        this.country = country;
    }
    public String getCountry(){
        return country;
    }
    */
    public String getInfo(){
        return "name="+name+",Country="+country;
    }
}

2018.04.13
·内部类:在一个类的内部定义的类
内部类属于外部类的一个成员,所以内部类可以直接使用外部类的成员

class Outer{
    int num = 66;
    class Inner{//成员内部类
        public void show(){
            System.out.println(num);
        }
    }
    public void fun(){
        Inner in = new Inner();
        in.show();
    }
}
class Test{
    public static void main(String[] args){
        Outer out = new Outer();
        out.fun();
        //直接创建内部类对象
        Outer.Inner in = new Outer().new Inner();
        in.show();
    }
}

//静态内部类:
//内部类中含有静态成员,内部类必须是静态的
class Outer{
    static int num = 66;
    static class Inner{//成员内部类
        public static void show(){
            System.out.println(num);
        }
    }
    public void fun(){
        Inner in = new Inner();//因为show方法是非静态的
        in.show();
    }
}
class Test{
    public static void main(String[] args){
        // Outer out = new Outer();
        // out.fun();

        // Outer.Inner in = new Outer.Inner();
        // in.show();

        Outer.Inner.show();
    }
}

//静态内部类:
//内部类中含有静态成员,内部类必须是静态的
//内部类得到外部类对象中的成员 外部类名.this.成员
//内部类得到外部类对象           外部类名.this
class Outer{
    int num = 5;
    class Inner{//成员内部类
        int num = 6;
        public void show(){
            int num = 7;    //num 7 this.num 6 Outer.this.num 5
            System.out.println(Outer.this.num);
        }
    }
    public void fun(){
        Inner in = new Inner();//因为show方法是非静态的
        in.show();
    }
}
class Test{
    public static void main(String[] args){
        Outer out = new Outer();
        out.fun();
    }
}

从内部类中访问本地变量,需要被声明为最终类型

声明为static的方法有以下几条限制:
它们仅能调用其他的static方法
它们只能访问static数据
它们不能以任何方式引用this或super(因为this代表的是调用这个函数的对象的引用,而静态方法是属于类的,不属于对象,静态方法成功加载后,对象还不一定存在)

·代码块
1、普通代码块,在方法中写的代码块
2、构造块,在类中定义的代码块,在创建对象时被调用,优于构造方法执行
3、静态代码块,在类中使用static声明的代码块称为静态代码块
在第一次使用的时候被调用(创建对象),只会执行一次,优于构造块执行
我们在项目开发中,通常会使用静态代码块来初始化只调用一次的数据。
小结:重点会使用的顺序是静态代码块,普通代码块,同步代码块,构造块

·单例设计模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点
1、构造方法私有化
2、声明一个本类对象
3、给外部提供一个静态方法获取对象实例

两种实现方式:
1、饿汉式:在类被夹在后对象被创建,一直到程序结束释放
2、懒汉式:在第一次调用getInstance方法时,对象被创建,到程序结束后释放

在项目中为什么要使用单例,单例有什么好处?
1、在设计一些工具类的时候(通常工具类,只有功能方法,没有属性)
2、工具类可能会被频繁调用

目的是为了节省重复创建对象所带来的内存消耗,从而提高效率
能不能使用构造方法私有化+静态方法来替代单例?

public class Test{
    public static void main(String[] args){
        // Singleton1 s = new Singleton1;
        // Singleton1 s = Singleton1.getInstance();
        // s.print();
        // Singleton1 s1 = Singleton1.getInstance();
        // s1.print();

        Singleton2 s = Singleton2.getInstance();
        s.print();
        Singleton2 s1 = Singleton2.getInstance();
        s1.print();

        System.out.println(s==s1);
    }
}

//饿汉式:占用内存的时间长,提高效率
class Singleton1{
    private Singleton1(){}
    private static Singleton1 s = new Singleton1();
    public static Singleton1 getInstance(){
        return s;
    }
    public void print(){
        System.out.println("测试方法");
    }
}

//懒汉式:占用内存的时间短,效率低(懒加载,延迟加载)
//在多线程访问时,会有安全问题
class Singleton2{
    private Singleton2(){}
    private static Singleton2 s;
    public static Singleton2 getInstance(){
        if(s==null){
            s = new Singleton2();
        }
        return s;
    }
    public void print(){
        System.out.println("测试方法");
    }
}

//使用构造方法私有化+静态方法来实现工具类,比如Math
//占用的内存比较大,建议使用单例设计模式
class Tools{
    private Tools(){}
    public static void print1(){

    }
    public static void print2(){

    }
}

·对象数组与管理
1、对象数组就是数组里的每个元素都是类的对象,赋值时先定义对象,然后将对象直接赋给数组
示例:
Chicken[] cs = new Chicken[10];

使用对象数组实现多个Chicken的管理

/**
    动态数组:
    1、数组是一种线性结构
    2、数组不适合做删除插入等操作,适合添加,查找,遍历
*/
import java.util.Arrays;
public class Test{
    public static void main(String[] args){
        ChickenManager cm = new ChickenManager(5);
        //添加
        cm.add(new Chicken(1,"小小",10));
        cm.add(new Chicken(2,"小二",8));
        cm.add(new Chicken(3,"小三",6));
        cm.add(new Chicken(4,"小红",2));
        cm.add(new Chicken(5,"小奋",4));

        cm.add(new Chicken(6,"小斗",1));
        System.out.println("数组的长度是"+cm.length());

        System.out.println("--------printAll---------");
        cm.printAll();

        System.out.println("--------find---------");
        Chicken c = cm.find(5);
        c.print();

        System.out.println("--------update---------");
        cm.update(new Chicken(1,"下蛋公鸡",20));
        cm.printAll();

        System.out.println("--------delete---------");
        cm.delete(4);
        cm.printAll();
    }
}

//小鸡管理类
class ChickenManager{
    private Chicken[] cs = null;
    private int count = 0;//记录当前数组的元素个数(下标)

    public ChickenManager(int size){
        if(size>0){
            cs = new Chicken[size];
        }else{
            cs = new Chicken[5];
        }
    }
    public int length(){
        return cs.length;
    }

    //添加:实现动态数组
    public void add(Chicken c){
        if(count>=cs.length){//数组已满,需要扩充
            //算法1:扩充原来数组大小的一半 cs.length*3/2+1
            //算法2:扩充原来数组的一倍 cs.length*2
            int newLen = cs.length*2;
            cs = Arrays.copyOf(cs,newLen);
        }
        cs[count] = c;
        count++;
    }
    //删除
    public void delete(int id){
        for(int i=0;i<count;i++){
            if(cs[i].getId()==id){
                //找到了要删除的对象,把该对象之后的对象向前移动一位
                for(int j=i;j<count-1;j++){
                    cs[j] = cs[j+1];
                }
                //把最后一个对像赋值为空(删除)
                cs[count-1] = null;
                count--;//下标-1
                break;
            }
        }
    }
    //更新
    public void update(Chicken c){
        Chicken temp = find(c.getId());
        if(temp!=null){
            temp.setName(c.getName());
            temp.setAge(c.getAge());
        }
    }
    //查找
    public Chicken find(int id){
        for(int i=0;i<count;i++){
            if(cs[i].getId()==id){
                return cs[i];
            }
        }
        return null;
    }
    //输出所有
    public void printAll(){
        for(int i=0;i<count;i++){
            cs[i].print();
        }
    }
}

//小鸡类(数据对象) value object(VO)
class Chicken{
    private int id;
    private String name;
    private int age;
    public Chicken(){}//一般情况下最好保留默认构造方法
    public Chicken(int id,String name,int age){
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public void setId(int id){
        this.id = id;
    }
    public int getId(){
        return id;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public int getAge(){
        return age;
    }
    public void print(){
        System.out.println("id="+id+",name="+name+",age="+age);
    }
}

—-面向对象(下)—-
·继承:继承是从已有的类创建新类的过程,继承一个父类,只能继承非私有的数据(属性和方法)
1、继承是面向对象三大特征之一
2、被继承的类称为父类(超类),继承父类的类称为子类(派生类)
3、继承是指一个对象直接使用另一对象的属性和方法
4、通过继承可以实现代码重用

2018.04.20
protected(受保护的访问权限修饰符,在继承关系中使用,在父类中使用protected修饰的属性或方法可以被子类继承)

继承的限制约定:
1、java只能实现单继承,也就是一个类只能有一个父类
2、允许多层继承,即:一个子类可以有一个父类,一个父类还可以有其他的父类
3、继承只能继承非私有的属性和方法
4、构造方法不能被继承
创建子类对象时,父类的构造方法也会被调用,为什么?
因为子类要使用到父类的数据,那么就要通过父类的构造方法来初始化数据
创建子类的时会调用父类的默认构造方法

public class Test{
    public static void main(String[] args){
        HomeDog homeDog = new HomeDog("旺财");
        homeDog.print();
    }
}

class Dog{
    protected String name;
    private String sex;
    public Dog(String name,String sex){
        this.name = name;
        this.sex = sex;
        System.out.println("我是dog的构造方法");
    }
    protected void eat() throws Exception{
        System.out.println("吃饭");
    }
}

class HomeDog extends Dog{
    public HomeDog(String name){
        super(name,"公");//只能在第一句
        this.name = name;
        System.out.println("我是homedog的构造方法");
    }
    public void print(){
        System.out.println(name+"我是一只家狗");
    }
    //重写父类的方法
    protected void eat() throws Exception{
        System.out.println("我是家狗,我喜欢吃骨头");
    }
}

class HuskyDog extends Dog{
    public HuskyDog(){
        super("哈士奇","母");//只能在第一句
        System.out.println("我是huskydog的构造方法");
    }
    public void show(){
        System.out.println(name+"我是husky,我能跳舞");
    }
    //重写父类的方法
    public void eat(){
        System.out.println("我是husky,我喜欢吃鸡肝");
    }
}

·继承小结:
1、继承是发生在多个类之间
2、继承使用关键字extends
3、Java只能单继承,允许多层继承
4、被继承的类叫父类(超类),继承父类的类叫子类(派生类)
5、在父类中的非私有属性和方法可以被子类继承
6、protected(受保护的访问权限修饰符),修饰的属性或方法可以被子类继承
7、构造方法不能被继承
8、创建对象会调用构造方法,调用构造方法不一定就是创建对象
9、实例化子类对象,会先调用父类的构造方法,如果父类中没有默认的构造方法,那么子类必须显示的通过super(…)来调用父类的带参构造方法,super也只能在子类构造方法中的第一句

·继承的好处:
1、提高代码的重用性
2、提高代码的维护性
3、让类与类之间产生关系,是多态的前提

继承的缺点:
增强了类与类之间的耦合性

开发原则:高内聚,低耦合

·子类的实例化过程
在子类进行实例化操作的时候,首先会先让其父类进行初始化操作。之后子类再自己进行实例化操作。

·子类的实例化过程:
子类实例化时会先调用父类的构造方法
如果父类中没有默认的构造方法,在子类的构造方法中必须显示的调用父类的构造方法

·结论:
构造方法只能用于初始化类中的字段以及执行一些初始化代码
调用构造方法并不代表会生成对象

·方法重写(overriding method)
在java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想做一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
在子类和父类中,重写方法后,在调用时,会调用谁的方法,以创建的对象类型为准,

·关于方法重写的一些特性:
1、发生在子父类中,方法重写的两个方法返回值、方法名、参数列表必须完全一只(子类重写父类的方法)
2、子类抛出的异常不能超过父类相应方法抛出的异常(子类异常不能大于父类异常)
3、子类方法的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)
4、父类中的方法若使用private、static、final任意修饰符修饰,那么,不能被子类重写

·为什么要重写方法?或者方法重写的目的是什么?
若子类从父类中继承过来的方法,不能满足子类特有的需求时,子类就需要重写父类中相应的方法,方法的重写也是程序扩展的体现

·面试题:overloading与overriding的区别?
overloading:方法的重载,发生在同一个类中,方法名相同,参数列表不同,返回值无关
overriding:方法的重写,发生在子父类中,方法名相同,参数列表相同,返回值相同,子类的访问权限修饰符要大于或等于父类的访问修饰符,子类的异常声明必须小于或等于父类的异常声明。如果方法被private,static,final修饰,那么不能被重写

·super可以完成以下的操作:
1、使用super调用父类中的属性,可以从父类实例处获得信息
2、使用super调用父类中的方法,可以委托父类对象帮助完成某件事情
3、使用super调用父类中的构造方法(super(实参)形式),必须在子类构造方法的第一条语句,调用父类中相应的构造方法,若不显示的写出来,默认调用父类的无参构造方法,比如:super();

this表示当前对象
使用super来调用父类的属性,方法和构造方法

·继承的应用示例

/**
    实现一个化妆品商城中的化妆品管理
    1、定义一个化妆品商城中的化妆品管理
    2、定义一个化妆品管理类(CosmeticManager)
    (1)实现进货功能
    (2)可以输出所有化妆品信息功能
    3、使用继承实现一个可按单价排序输出所有化妆品的功能
    4、使用继承实现一个只输出进口化妆品的功能
*/

public class Test{
    public static void main(String[] args){
        ImportCosmeticManager cm = new ImportCosmeticManager();
        cm.add(new Cosmetic("香奈儿","进口",1000));
        cm.add(new Cosmetic("圣罗兰","进口",800));
        cm.add(new Cosmetic("大宝","国产",20));
        cm.add(new Cosmetic("万紫千红","国产",15));
        cm.printInfo();
    }
}

//可输出进口化妆品的管理类
class ImportCosmeticManager extends CosmeticManager{
    public void printInfo(){
        //比较两个字符串的值是否相等,不能使用==(对于引用类型,==比较的是地址),使用equals()
        for(int i=0;i<count;i++){
            if("进口".equals(cs[i].getType())){
                System.out.println(cs[i].getInfo());
            }
        }
    }
}

//可按单价排序的化妆品管理类
class SortCometicManager extends CosmeticManager{
    //排序输出所有产品
    public void printInfo(){
        Cosmetic[] temp = java.util.Arrays.copyOf(cs,count);

        Cosmetic c = null;
        for(int i=0;i<temp.length-1;i++){
            for(int j=0;j<temp.length-i-1;j++){
                if(temp[j].getPrice()>temp[j+1].getPrice()){
                    c = temp[j];
                    temp[j] = temp[j+1];
                    temp[j+1] = c;
                }
            }
        }

        for(Cosmetic cosmetic: temp){
            System.out.println(cosmetic.getInfo());
        }
    }
}

//化妆品管理类
class CosmeticManager{
    protected Cosmetic[] cs = new Cosmetic[4];
    protected int count = 0;
    //进货功能
    public void add(Cosmetic c){
        int size = cs.length;
        if(count>=cs.length){
            int newLen = size*2;
            cs = java.util.Arrays.copyOf(cs,newLen);
        }
        cs[count] = c;
        count++;
    }

    //输出所有产品
    public void printInfo(){
        for(int i=0;i<count;i++){
            System.out.println(cs[i].getInfo());
        }
    }
}

class Cosmetic{
    private String name;//品牌
    private String type;//进口或国产
    private int price;//单价
    public Cosmetic(){}
    public Cosmetic(String name,String type,int price){
        this.name = name;
        this.type = type;
        this.price = price;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setType(String type){
        this.type = type;
    }
    public String getType(){
        return type;
    }
    public void setPrice(int price){
        this.price = price;
    }
    public int getPrice(){
        return price;
    }
    public String getInfo(){
        return "name="+name+",type="+type+",price="+price;
    }
}

·final关键字
使用final关键字可以完成以下的操作:
1、使用final关键字声明一个常量,修饰属性或者修饰局部变量(最终变量),也成为常量
2、使用final关键字声明一个方法,该方法是最终方法,且只能被子类继承,但是不能被子类重写
3、使用final关键字声明一个类,该类就转变为最终类,没有子类的类,final修饰的类无法被继承
4、在方法参数中使用final,在该方法内部不能修改参数的值(在内部类详解)

/**
    final关键字
    1、使用final声明一个属性,就是常量,常量的命名规则建议使用全大写,常量必须在定义时完成初始化,或在构造器中初始化
    2、使用final声明的方法,不能被子类重写,只能被继承
    3、使用final关键字声明一个类,该类就转变为最终类,没有子类的类,final修饰的类无法被继承
    4、在方法参数中使用final,在该方法内部不能修改参数的值(在内部类详解)
    5、如果final修饰的是对象,就是对象的引用不能改变,引用里对应的值可以改变
*/
public class Test{
    public static void main(String[] args){
        System.out.println(Constant.PERSON_NUM);
        FinalClass fc = new FinalClass();
        fc.setLength(10);
    }
}

//常量类(工具类的一种):在实际项目开发中,常量类通常用于定义项目中一些公共的,不变的数据
final class Constant{//最终类无法被继承
    public static final int PERSON_NUM = 10;//人数
}

class FinalClass{
    public final int DAY_NUMBER;//工作天数

    public FinalClass(){
        DAY_NUMBER = 22;
    }

    public final void print(){
        System.out.println("我是final方法");
    }

    public void setLength(final int size){
        //size++;//不能分配最终参数
        System.out.println(size);
    }
}

class SubClass extends FinalClass{
    /*public final void print(){
        System.out.println("我是final方法");
    }*/
}

·抽象类
抽象类的基本概念:
(1)很多具有相同特征的行为的对象可以抽象为一个类;很多具有相同特征和行为的类可以抽象为一个类
(2)使用abstract关键字声明的类为抽象类

/**
    抽象类:用abstract关键字声明的类称为抽象类
*/
public class Test{
    public static void main(String[] args){
        Man man = new Man();
        man.move();
        man.eat();
        Woman woman = new Woman();
        woman.move();
        woman.eat();
    }
}

abstract class Animal{
    public abstract void move();//方法的声明,抽象方法只有声明,没有实现

}

abstract class Person extends Animal{
    public abstract void eat();
}

//继承抽象类的具体类必须实现所有抽象方法
class Man extends Person{
    public void move(){
        System.out.println("我是男人,我爱跑步");
    }
    public void eat(){
        System.out.println("我是男人,我爱吃肉");
    }
}

class Woman extends Person{
    public void move(){
        System.out.println("我是女人,我爱逛街");
    }
    public void eat(){
        System.out.println("我是女人,我爱吃水果");
    }
}

·抽象类的规则:
1、抽象类可以没有抽象方法,有抽象方法的类必须是抽象类
2、非抽象类继承抽象类必须实现所有抽象方法
3、抽象类可以继承抽象类,可以不实现抽象方法
4、抽象类可以有方法实现和属性
5、抽象类不能被实例化
6、抽象类不能声明为final
7、抽象类可以有构造方法,在继承关系里面,调用父类的构造方法,不一定是new对象,也有可能是为了初始化参数

·接口
接口的定义格式:
interface 接口名称{
全局常量;
抽象方法;
}

接口的概念
1、接口是一组行为的规范,定义,没有实现(jdk1.8可以有默认方法)
2、使用接口,可以让我们的程序更加利于变化
3、接口是面向对象变成体系中的思想精髓之一
4、面向对象设计法则:基于接口编程

public class Test{
    public static void main(String[] args){
        Girl mm = new Girl("玛丽");
        mm.sleep();
        mm.eat();
        mm.run();
  mm.print();
    }
}

interface IEat{
    //public abstract void eat();//接口中只能定义抽象方法
    void eat();//接口中定义的方法没有声明修饰符,默认为public abstract
    // public static final int NUM = 10;//在接口中定义一个常量
    int NUM = 10;//常量
    //默认方法jdk1.8后新特性,可以被所有实现类继承
    public default void print(){
        System.out.println("eat");
    }
}

interface IRun{
    void run();
}

//接口之间可以多继承(注意:类只能单继承)
interface ISleep extends IEat,IRun{
    void sleep();
}

//具体类实现接口必须实现接口的所有方法
class Girl implements ISleep,IEat{
    private String name;
    public Girl(String name){
        this.name = name;
    }
    public void sleep(){
        System.out.println("我爱睡觉");
    }
    public void eat(){
        System.out.println("我是"+name+",我爱吃猪脚");
    }
    public void run(){
        System.out.println("吃完就跑");
    }
}

·接口的使用规则:
1、定义一个接口,使用interface关键字
2、在一个接口中,之恩那个定义常量,抽象方法,jdk1.8后可以定义默认的实现方法
3、接口可以继承多个接口:extends xxx,xxx
4、一个具体类实现接口使用implements关键字
5、一个类可以实现多个接口
6、抽象类实现接口可以不实现接口的方法
7、在接口中定义的方法没有声明访问权限修饰符,默认为public abstract
8、接口不能有构造方法
9、接口不能被实例化

·面向对象设计原则:
1、对修改关闭,对扩展开放
2、面向接口编程

·面向对象的多态性
多态性是面向对象三大特性之一
什么是多态性?
对象在运行过程中的多种形态

多态性我们大概可以分为两类:
1、方法的重载与重写
2、对象的多态性

例如:
//用父类的引用指向子类对象(用大的类型去接受小的类型,向上转型,自动转换)
Chicken home = new HomeChicken();

结论:
在编程时针对抽象类型的编写代码,成为面向抽象编程(或面向接口编程)
父类通常都定义为抽象类、接口

/**
    多态性:
*/
public class Test{
    public static void main(String[] args){
        //类型转换:用父类的引用指向子类对象(用大的类型来表示小的类型),自动转换(向上转型)
        Chicken hc = new HomeChicken("小鸡鸡");
        // hc.eat();

        Chicken yc = new YeChicken("大鸡鸡");
        // yc.eat();

        // hc = yc;
        // hc.eat();
        //eat(hc);
        //eat(yc);

        Chicken jjc = new JianJiaoChicken("尖叫鸡");
        eat(jjc);
        eat(hc);
        eat(yc);
    }

    //抽象(粒度)面向抽象编程(面向接口编程)
    public static void eat(Chicken c){
        System.out.println("鸡吃饭");
        c.eat();
        //当我们需要把父类的实例强制转换为子类引用时,为了避免类型转换一场java.lang.ClassCastException
        //那么我们需要在转换之前作类型检查(判断)
        if(c instanceof JianJiaoChicken){//成立的条件是,对象本身及对象的父类型,都可以通过类型检查
            JianJiaoChicken jjc = (JianJiaoChicken) c;//大的类型转换为小的类型,强制转换(向下转型)
            jjc.song();
        }
    }
}

//鸡
abstract class Chicken{
    private String name;
    public Chicken(){}
    public Chicken(String name){
        this.name = name;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public abstract void eat();
}

//家鸡
class HomeChicken extends Chicken{
    public HomeChicken(String name){
        super(name);
    }
    public void eat(){
        System.out.println(this.getName()+",我爱吃米");
    }
}

//野鸡
class YeChicken extends Chicken{
    public YeChicken(String name){
        super(name);
    }
    public void eat(){
        System.out.println(this.getName()+",我爱吃虫子");
    }
}

//尖叫鸡
class JianJiaoChicken extends Chicken{
    public JianJiaoChicken(String name){
        super(name);
    }
    public void eat(){
        System.out.println(this.getName()+",我不吃东西");
    }
    public void song(){
        System.out.println("唧唧复唧唧,我是尖叫鸡");
    }
}

·对象的多态性:
对象多态性是从继承关系中的多个类而来,
向上转型:将子类实例转为父类引用
格式:父类 父类对象 = 子类示例; 自动转换
以基本数据类型操作为例:int i = ‘a’;
(因为char的容量比int小,所以可以自动完成)

向下转型:将父类实例转为子类实例
格式:子类 子类对象 = (子类)父类示例; 强制转换
以基本数据类型操作为例:char c = (char)97;
因为整型是4个字节比char 2个字节要大,所以需要强制完成

·多态性小结
1、方法的重载与重写就是方法的多态性表现
2、多个子类就是父类中的多种形态
3、父类引用可以指向子类对象,自动转换
4、子类对象指向父类引用需要强制转换(注意:类型不对会报异常)
5、在实际开发中尽量使用父类引用(更利于扩展)

2018.04.21
·instanceof关键字
instanceof是用于检查对象是否为指定的类型,通常在把父类引用强制转换为子类引用时要使用,以避免发生类型转换一场(ClassCastException)
该语句一般用于判断一个对象是否为某个类的实例,是返回true,否返回false

·父类的设计法则
通过instanceof关键字,我们可以很方便的检查对象的类型,但如果一个父类的子类过多,这样的判断还是显得很繁琐,那么如何去设计一个父类呢?
1、父类通常情况下都设计为抽象类或接口,其中优先考虑接口,如接口不能满足才考虑抽象类
2、一个具体的类尽可能不去继承另一个具体类,这样的好处是无需检查对象是否为父类的对象

例子参考上例

·模版方法模式
定义一个操作中的算法的骨架,而将一些可变部分的实现延迟到子类中。模版方法模式使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定的步骤

public class Test{
    public static void main(String[] args){
        UserManager um = new UserManager();
        um.action("admin","add");
    }
}

abstract class BaseManager{

    public void action(String name,String method){
        if("admin".equals(name)){
            execute(method);
        }else{
            System.out.println("你没有操作权限,请联系管理员");
        }
    }

    public abstract void execute(String method);
}

class UserManager extends BaseManager{
    public void execute(String method){

        //用户是否登录的验证
        //验证成功后才可以执行以下操作
        if("add".equals(method)){
            System.out.println("执行了添加操作");
        }else if("del".equals(method)){
            System.out.println("执行了删除操作");
        }
    }
}

·接口应用——策略模式
策略模式,定义了一系列的算法,将每一种算法封装起来并可以相互替换使用,策略模式让算法独立于使用它的客户应用而独立变化

OO设计原则:
1、面向接口变成(面向抽象编程)
2、封装变化
3、多用组合,少用继承

/**
    策略模式,定义了一系列的算法,将每一种算法封装起来并可以相互替换使用,策略模式让算法独立于使用它的客户应用而独立变化

    把可变的行为抽象出来,这样的好处是这些行为可以在真正的使用时可以相互替换
*/
public class Test{
    public static void main(String[] args){
        BaseService user = new UserService();
        user.setISave(new FileSave());
        user.add("user");
    }
}

//把可变的行为抽象出来,定义一系列的算法
interface ISave{
    public void save(String data);
}

class FileSave implements ISave{
    public void save(String data){
        System.out.println("把数据保存到文件中"+data);
    }
}

class NetSave implements ISave{
    public void save(String data){
        System.out.println("把数据保存到网络上"+data);
    }
}

abstract class BaseService{

    private ISave iSave;
    public void setISave(ISave iSave){
        this.iSave = iSave;
    }

    public void add(String data){
        System.out.println("检查数据合法性...");
        iSave.save(data);
        System.out.println("数据保存完毕");
    }
}

class UserService extends BaseService{

}
·ObjectObject类是类层次结构的根类
每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的方法
所有类都是Object的子类

·public String toString()方法
返回该对象的字符串表示。
同上,toString方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明且易于读懂的信息表达式。建议所有子类都重写此方法。

·public boolean equals(Object obj)
指示其他某个对象是否与此对象“相等”。equals方法在非空对象引用上实现相等关系:
自反性:对于任何非空引用值x,x.equals(x)都应返回true
对称性:对于任何非空引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true
传递性:对于任何非空引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)应返回true
一致性:对于任何非空引用值x和y,多次调用x.equals(y)始终返回true或始终返回false,前提是对象上equals比较中所用的信息没有被修改
对于任何非空引用值x,x.equals(null)都应返回false

·protected void finalize()throws Throwable
当垃圾回收器确定不存在该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写finalize方法,以配置系统资源或执行其他清除,不建议重写此方法

·public final Class<?> getClass()
返回此Object的运行时类
/**
    Object对象
*/
public class Test{
    public static void main(String[] args){
        Student s = new Student(1,"飞飞",18);
        // System.out.println(s.toString());
        System.out.println(s);//直接打印对象的名字,默认调用其toString()方法
        Student s2 = new Student(1,"飞飞",18);
        boolean b = s.equals(s2);
        System.out.println("student equals:"+b);

        String str1 = new String("beibei");
        String str2 = new String("beibei");
        System.out.println(str1==str2);//false
        System.out.println(str1.equals(str2));//true

        System.out.println(s.getClass()==s2.getClass());//true
    }
}

class Student{
    private String name;
    private int sid;
    private int age;
    public Student(){}
    public Student(int sid,String name,int age){
        this.sid = sid;
        this.name = name;
        this.age = age;
    }
    //重写Object类中的toString方法
    public String toString(){
        return "sid="+sid+",name="+name+",age="+age;
    }
    public boolean equals(Object obj){
        if(this==obj){
            return true;
        }
        if(obj instanceof Student){
            Student s = (Student)obj;
            if(!this.name.equals(s.name)){
                return false;
            }
            if(this.sid!=sid){
                return false;
            }
            if(this.age!=s.age){
                return false;
            }
            return true;
        }
        return false;
    }
}

·简单工厂模式
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单使用的模式

/**
    简单工厂模式
*/
public class Test{
    public static void main(String[] args){
        //使用者和被使用者两者之间,耦合,产生了以来,当被使用者改变时,会影响使用者
        // Product phone = new Phone();
        //使用工厂模式来降低两者之间的依赖
        Product phone = ProductFactory.getProduct("phone");
        if(null!=phone){
            phone.work();
        }
    }
}

//工厂类
class ProductFactory{
    public static Product getProduct(String name){
        if("phone".equals(name)){
            return new Phone();
        }else if("computer".equals(name)){
            return new Computer();
        }else{
            return null;
        }
    }
}

interface Product{
    public void work();
}

class Phone implements Product{
    public void work(){
        System.out.println("手机开始工作");
    }
}

class Computer implements Product{
    public void work(){
        System.out.println("电脑开始工作");
    }
}

·静态代理模式
代理模式:为其他对象提供一种代理以控制对这个对象的访问
代理模式说白了就是“真实对象”的代表,在访问对象时引入一定程度的间接性,因为这种间接性可以附加多种用途

/**
    静态代理模式
*/
public class Test{
    public static void main(String[] args){
        Action userAction = new UserAction();
        ActionProxy proxy = new ActionProxy(userAction);
        proxy.doAction();
    }
}

interface Action{
    public void doAction();
}

class ActionProxy implements Action{

    private Action target;//被代理的对象

    public ActionProxy(Action target){
        this.target = target;
    }

    //执行操作
    public void doAction(){
        long startTime = System.currentTimeMillis();
        target.doAction();//执行真正的业务
        long endTime = System.currentTimeMillis();
        System.out.println("共耗时:"+(endTime-startTime));
    }
}

class UserAction implements Action{
    public void doAction(){
        System.out.println("用户开始工作...");
    }
}

·适配器模式:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

OO设计原则:
1、面向接口编程(面向抽象编程)
2、封装变化
3、多用组合,少用继承
4、对修改关闭,对扩展开放

/**
    适配器模式
*/
public class Test{
    public static void main(String[] args){
        PowerA powerA = new PowerAImpl();
        // work(powerA);

        PowerB powerB = new PowerBImpl();
        //work(powerB);
        Adapter adapter = new Adapter(powerB);
        work(adapter);
    }

    public static void work(PowerA a){
        System.out.println("正在连接...");
        a.insert();
        System.out.println("工作结束");
    }
}

interface Animal{
    public void sing();
    public void cry();
    public void run();
    public void swim();
}

//适配器类2
abstract class AnimalFunction{
    public void sing(){}
    public void cry(){}
    public void run(){}
    public void swim(){}
}

//只关注跑,不想实现其他方法
class Dog extends AnimalFunction{
    public void run(){
        System.out.println("我是狗,我会跑");
    }
}


//适配器类1
class Adapter implements PowerA{
    private PowerB powerB;
    public Adapter(PowerB powerB){
        this.powerB = powerB;
    }
    public void insert(){
        powerB.connect();
    }
}

interface PowerB{
    public void connect();
}

class PowerBImpl implements PowerB{
    public void connect(){
        System.out.println("电源B开始工作");
    }
}

interface PowerA{
    public void insert();
}
class PowerAImpl implements PowerA{
    public void insert(){
        System.out.println("电源A开始工作");
    }
}

·内部类详解
内部类就是在一个类的内部定义的类

·成员内部类

class Outer{
    class Inner{}
}

编译上述代码会产生两个文件:
Outer.class 和 Outer$Inner.class

在外部创建内部类对象
内部类除了可以在外部类中产生实例化对象,也可以在外部类的外部来实例化。
那么,根据内部类生成的*.class文件:Outer I n n e r . c l a s s ”符号在程序运行时将替换成“.”
所以内部类的访问:通过“外部类.内部类”的形式表示

Outer out = new Outer();//产生外部类实例
Inner in = null;//声明内部类对象
in = out.new Inner();//实例化内部类对象

·方法内部类
内部类除了可以作为一个类的成员外,还可以把类放在方法内定义
注意:
1、方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化
2、方法内部类对象不能使用该内部类所在方法的非final局部变量

·静态内部类
在一个类内部定义一个静态内部类:
静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它。静态嵌套类仅能访问外部类的静态成员和方法

·匿名内部类
匿名内部类就是没有名字的内部类
匿名内部类的三种情况:
(1)继承式的匿名内部类
(2)接口式的匿名内部类
(3)参数式的匿名内部类

·在使用匿名内部类时,要记住以下几个原则:
(1)不能有构造方法(因为没有名字),只能有一个实例
(2)不能定义任何静态成员,静态方法(静态成员或方法直接通过类名.成员名或方法名调用,但匿名内部类没有名字)
(3)不能是public,protected,private,static
(4)一定是在new的后面,用其隐含实现一个接口或实现一个类
(5)匿名内部类为局部的,所以局部内部类的所有限制都对其生效

问题:局部内部类访问局部变量必须用final修饰,为什么?
当调用这个方法时,局部变量如果没有用final修饰,他的声明周期和方法的生命周期是一样的,当方法被调用时会入栈,方法结束后即弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,显然已无法使用了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也就可以继续使用了
注意:在jdk1.8中取消了在局部内部类中使用的变量必须显示的使用final修饰,编译器默认会为这个变量加上final

内部类的作用
每个内部类都能独立地继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的剞劂方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。

成员:
成员内部类
静态内部类

局部:
方法内部类
匿名内部类

依赖外部类对象的:成员内部类,方法内部类,匿名内部类
静态内部类不以来外部类的对象。所以我们在项目中优先考虑选择静态内部类(不会产生内存泄漏)

/**
    内部类:
    1、成员内部类:直接在类中定义的类
    2、方法内部类:在一个类中的方法定义一个类
        (1)、方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化
        (2)、方法内部类对象不能使用该内部类所在方法的非final局部变量
    3、静态内部类:在一个类内部定义一个静态修饰的内部类
        静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它。
        静态嵌套类仅能访问外部类的静态成员和方法
    4、·匿名内部类:匿名内部类就是没有名字的内部类
         匿名内部类的三种情况:
            (1)继承式的匿名内部类
            (2)接口式的匿名内部类
            (3)参数式的匿名内部类
*/
public class Test{
    public static void main(String[] args){
        Outer outer = new Outer();
        //在外部创建成员内部类的实例,因为成员内部类需要以来外部类的对象
        //通常情况下,不建议这样来实例化内部类的对象
        // Outer.Inner inner = outer.new Inner();
        // inner.print();

        outer.innerPrint();
        outer.show(11);

        Outer.Inner3 inner3 = new Outer.Inner3();
        inner3.print();

        outer.print1();
        outer.print2();

        //参数式匿名内部类
        outer.print3(new Eat(){
            public void eat(){
                System.out.println("参数式匿名内部类");
            }
        });
    }
}

class Outer{
    private String name = "Outer";

    //----成员内部类----
    //建议在外部类中定义一个方法,对外提供访问内部类的接口
    public void innerPrint(){
        Inner inner = new Inner();
        inner.print();
    }
    //成员内部类
    private class Inner{
        public void print(){
            System.out.println("成员内部类"+name);
        }
    }

    //----方法内部类----
    //show方法的局部变量或方法的参数,实际必须是常量 final
    public void show(final int y){
        final int x = 10;
        class Inner2{
            public void print(){
                // x++;//从内部类引用的本地变量必须是最终变量或实际上的最终变量
                System.out.println("方法内部类"+x+y+name);
            }
        }
        Inner2 inner2 = new Inner2();
        inner2.print();
    }

    //----静态内部类----
    //无法从静态上下文中引用非静态变量
    static class Inner3{
        public void print(){
            System.out.println("静态内部类");
        }
    }

    //----匿名内部类----
    //继承式
    public void print1(){
        Cat cat = new Cat(){
            public void eat(){
                System.out.println("cat:继承式匿名内部类");
            }
        };//注意分号不能省略
        cat.eat();
    }
    //接口式
    public void print2(){
        Eat eat = new Eat(){
            public void eat(){
                System.out.println("eat:接口式匿名内部类");
            }
        };//注意分号不能省略
        eat.eat();
    }

    //参数式
    public void print3(Eat eat){
        eat.eat();
    }
}

abstract class Cat{
    public abstract void eat();
}

interface Eat{
    public void eat();
}


//变相实现多继承
/**
class A extends B{
    class D extends C{

    }
}
*/

2018.04.22
·递归算法
在链表数据结构中,我们需要使用到递归算法
递归算法是一种直接或者间接地调用自身算法的过程。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解

public class Test{
    public static void main(String[] args){
        int result = jiecheng2(10);
        System.out.println(result);
    }

    public static int jiecheng1(int num){
        int result = num;
        int i = num-1;
        do{
            result = result * i;
            i--;
        }while(i>1);
        return result;
    }

    //递归算法。方法本身调用自己
    //1、递归必须要有出口
    //2、递归内存消耗大,容易发生内存溢出
    //3、层次调用越多,越危险
    public static int jiecheng2(int num){
        if(num==1)return 1;
        return num*jiecheng2(num-1);
    }
}

·数据结构之链表


/**
    链表
    一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据
    而是在每一个节点里存的是下一个节点的指针(Pointer)

    链表与数组:线性数据结构
    数组适合查找,遍历,固定长度
    链表适合插入,删除,不宜过长,否则会导致遍历性能下降
*/
public class Test{
    public static void main(String[] args){

        NodeManager nm = new NodeManager();
        System.out.println("-----add-----");
        nm.add(5);
        nm.add(4);
        nm.add(3);
        nm.add(2);
        nm.add(1);
        nm.print();
        System.out.println("-----del-----");
        nm.del(3);
        nm.print();
        System.out.println("-----find-----");
        System.out.println(nm.find(1));
        System.out.println("-----update-----");
        nm.update(1,10);
        nm.print();
        System.out.println("-----update-----");
        nm.insert(1,20);
        nm.print();
    }
}

class NodeManager{
    private Node root; //根节点
    private int currentIndex = 0;//节点的序号,每次操作从0开始

    //添加
    public void add(int data){
        if(root==null){
            root = new Node(data);
        }else{
            root.addNode(data);
        }
    }
    //删除节点
    public void del(int data){
        if(root==null)return;
        if(root.getData()==data){
            root = root.next;
        }else{
            root.delNode(data);
        }
    }
    //打印所有
    public void print(){
        if(root!=null){
            System.out.print(root.getData()+"->");
            root.printNode();
            System.out.println();
        }
    }
    //查找节点是否存在
    public boolean find(int data){
        if(root==null)return false;
        if(root.getData()==data){
            return true;
        }else{
            return root.findNode(data);
        }
    }
    //修改节点
    public boolean update(int oldData,int newData){
        if(root==null)return false;
        if(root.getData()==oldData){
            root.setData(newData);
            return true;
        }else{
            return root.updateNode(oldData,newData);
        }
    }
    //index前插入节点
    public void insert(int index,int data){
        if(index<0)return;
        currentIndex = 0;
        if(index==currentIndex){
            Node newNode = new Node(data);
            newNode.next = root;
            root = newNode;
        }else{
            root.insertNode(index,data);
        }
    }

    private class Node{
        private int data;
        private Node next; //把当前类型作为属性
        public Node(int data){
            this.data = data;
        }
        public void setData(int data){
            this.data = data;
        }
        public int getData(){
            return data;
        }
        //添加节点
        public void addNode(int data){
            if(this.next==null){
                this.next = new Node(data);
            }else{
                this.next.addNode(data);
            }
        }
        //删除节点
        public void delNode(int data){
            if(this.next!=null){
                if(this.next.data==data){
                    this.next = this.next.next;
                }else{
                    this.next.delNode(data);
                }
            }
        }
        //输出所有节点
        public void printNode(){
            if(this.next!=null){
                System.out.print(this.next.data+"->");
                this.next.printNode();
            }
        }
        //查找节点是否存在
        public boolean findNode(int data){
            if(this.next!=null){
                if(this.next.data==data){
                    return true;
                }else{
                    return this.next.findNode(data);
                }
            }
            return false;
        }
        //修改节点
        public boolean updateNode(int oldData,int newData){
            if(this.next!=null){
                if(this.next.data==oldData){
                    this.next.data = newData;
                    return true;
                }else{
                    return this.next.updateNode(oldData,newData);
                }
            }
            return false;
        }
        //插入节点
        public void insertNode(int index,int data){
            currentIndex++;
            if(index==currentIndex){
                Node newNode = new Node(data);
                newNode.next = this.next;
                this.next = newNode;
            }else{
                this.next.insertNode(index,data);
            }
        }
    }
}

·基本数据类型包装类
在java中有一个设计的原则“一切皆对象”,java中的基本数据类型就完全不符合这种设计思想,因为八种基本数据类型并不是引用数据类型,所以java中为了解决这样的问题,JDK1.5以后引入了八中基本数据类型的包装类。

八中包装类分为两大类型:
Number:Integer、Short、Long、Double、Float、Byte都是Number的子类表示是一个数字
Object:Character,Boolean都是Object的直接子类

基本数据类型      包装类
int             Integer
char            Character
float           Float
double          Double
boolean         Boolean
byte            Byte
short           Short
long            Long

装箱及拆箱操作
将一个基本数据类型转换为包装类,那么这样的操作称为装箱操作。讲一个包装类转换为一个基本数据类型,这样的操作称为拆箱操作

方法                  描述
byteValue()         Byte->byte
doubleValue()       Double->double
floatValue()        Float->float
intValue()          Integer->int
longValue()         Long->long
ShortValue()        Short->short

·转型操作
在包装类中,可以将一个字符串变为指定的基本数据类型,一般在输入数据时会使用较多
在Integer类中将String变为int型数据:public static int parseInt(String s)
在Float类中将String变为float型数据:public static float parseFloat(String s)

注意:转型操作时,字符串必须由数字组成,否则会出现错误

享元模式:它使用共享对象,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似对象;它适合用于当大量对象只是重复因而导致无法令人接受的使用大量内存。通常对象中的部分状态是可以分享的。常见的做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。

运用共享技术有效的支持大量细粒度的对象

public class Test{
    public static void main(String[] args){
        //把基本数据类型转换为包装类,称为自动装箱
        Integer i1 = new Integer(10);
        //把包装类转换为基本数据类型,称为自动拆箱
        int i2 = i1.intValue();

        Integer i3 = 10;//建议方式

        Integer i4 = new Integer("123");

        //把数值字符串转换为int类型
        String num1 = "12";
        int i5 = Integer.parseInt(num1);

        Integer i6 = Integer.valueOf(num1);
        int i7 = Integer.valueOf(num1);

        //面试题:
        Integer x1 = new Integer(10);
        Integer x2 = new Integer(10);
        System.out.println(x1==x2);//false   ==比较的是地址
        System.out.println(x1.equals(x2));//true   .equals比较的才是值

        Integer x3 = new Integer(128);
        Integer x4 = new Integer(128);
        System.out.println(x3==x4);//false
        System.out.println(x3.equals(x4));//true

        //享元模式
        //整数,java内认为在一个字节以内的数是经常会使用的数,利用享元模式将
        //一个字节以内的数都做了一个缓存,当你用直接赋值方式的时候,如果10这
        //个数已经被赋值了,那他会把10放到常量池中,当x6再要用到10的时候,他
        //先从常量池的共享元素中找10,如果有10,直接指向10,所以x5和x6指向的
        //是同一个对象,但128就不行,因为128超过了一个字节,常量池中没有这个
        //数,他会缓存一个字节以内的数,所以System.out.println(x9==x10);就是
        //true
        Integer x5 = 10;
        Integer x6 = 10;
        System.out.println(x5==x6);//true
        System.out.println(x5.equals(x6));//true

        Integer x7 = 128;
        Integer x8 = 128;
        System.out.println(x7==x8);//false
        System.out.println(x7.equals(x8));//true

        Integer x9 = 127;
        Integer x10 = 127;
        System.out.println(x9==x10);//true
        System.out.println(x9.equals(x10));//true
    }
}

·包与访问修饰符
包用于对多个java源文件的管理,就像我们的目录一样
定义一个包:
package com.vince;
该语句只能出现在代码中的第一句

package com.qfedu.sms.vo;

public class Student{
    private int id;
    private String name;
    private int age;
    //....getter and setter
}
访问修饰符   同一个类        同包      不同包子类   不同包非子类
public          √         √             √            √
protected       √         √             √
默认             √         √
private
·OO原则总结
(1)开闭原则
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭
(2)合成/聚合复用原则
新对象的某些功能在已创建好的对象里已实现,那么尽量用已有对象提供的功能,使之称为新对象的一部分,而不要再重新创建
(3)依赖倒置原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖对象
(4)接口隔离原则
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上
(5)迪米特法则
一个对象应该对其他对象保持最少的了解
(6)里氏替换原则
所以引用基类的地方必须能透明地使用其子类的对象
(7)单一职责原则
不要存在多余一个导致类变更的原因,即一个类只负责一项职责

猜你喜欢

转载自blog.csdn.net/qq_39776901/article/details/79846894
今日推荐