HeadFirstJava

0.子类是不会继承父类的构造方法的,但是会调用父类的构造方法,如果父类的构造器带有参数并且子类要调用父类有参构造方法的话,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造方法,并配以适当的参数列表调用,否则还是自动调用父类无参构造方法。

1.子类由自己向上面的父类找寻调用的方法,直至找到方法。(Wolf->Canine->Animal);

2.如果某个类继承了一个方法,那么说这个类就有了这个方法;子类会继承父类的实例变量和方法,任一类的成员包含有自己定义出的变量和方法再加上从父类所继承下来的任何东西。

3.能否继承的判断方法:IS-A测试(三角形是一个多边形;外科医生是一个医生;……),该测试还可以用于继承层次的任意地方;(虎是猫科动物,猫科动物是动物……)

4.super.methods();在子类中执行父类的方法,后续可以补充自己的代码,以达到扩充子类行为的结果。

5.关于私有成员变量无论父类中的成员变量是pirvate、public还是其它类型的,子类都会拥有(继承)父类中的这些成员变量。但是父类中的私有成员变量,无法在子类中直接访问,可以通过从父类中继承得到的protected、public方法(如getter、setter方法)来访问。个人认为这更好的提现了JAVA特性中的封装,而且符合软件工程的设计思想:低耦合。关于静态成员变量无论父类中的成员变量是静态的、还是非静态的,子类都会拥有父类中的这些成员变量。关于被子类覆盖的成员变量无论父类中的成员变量是否被子类覆盖,子类都会拥有父类中的这些成员变量。(节选自),这篇文章中“通过断点的watch可以看到在子类实例了父类后,无论是私有成员,还是final类型的私有成员,在子类里都可以被“看到(被继承)””,但是在书中解释为,public类型的成员会被继承,private类型的成员不会被继承;我理解应该是都可以继承,但是子类对于private类型的成员是没有访问权限的,只有通过父类设置的getter方法来访问。

6.使用继承的一些条件(不能滥用继承):

  1.当某一个类会比其父类更加具有特定意义是使用继承;

  2.在行为程序(实现程序代码)应该呗多个相同基本类型类所共享时,应该考虑使用继承;

  3.若两者之间的关系对于继承结构来说并不合理,则不要只是打算要重用其他类的程序代码二运用继承;

  4.如果两者之间不能通过IS-A测试就不要使用继承关系。

7.当定义出一组类的父型时,你可以用子型的任何类来填补任何需要或期待父型的位置。运用多态时,引用类型可以是实际对象类型的父类(Animal myDog = new Dog();)。参数和返回类型也可以时多态。向方法中传入一个父类的引用变量,那我们也可以传入子类的引用变量从而实现多态。(例如经常这样使用的List<String> list = new ArrayList<>();)

8.防止某个类被作为子类的三个方法:

  1.存取控制,就算没有私有类这个概念,但是我们可以把他标记为非公有,非公有的类只能被同一个包的类作为子类;

  2.使用final这个修饰符,这表示他是继承树的末端,不能被继承;

  3.是让类只有private类型的构造程序。

9.如果要防止特定的方法被覆盖,可以将方法标识上final这个修饰符。

10.理解覆盖(重写),重载(过载)。

  覆盖(重写):是覆盖(重写)父类中的方法。1.参数必须一样,且返回类型必须兼容。如果要重写父类中的方法,则方法中传入的参数必须和父类的一致,而不论父类声明的返回类型是什么,子类必须要声明返回一样的类型或者说是该类型的子类。(子类对象要保证能够执行父类的一切。)2.不能降低方法的存取权限,表示子类中覆盖的方法存取权必须相同或者更加开放。

  重载(过载):两个方法名称相同但是参数不同。重载与继承和多态无关。1.返回类型可以不同。2.不能只改变返回类型,如果只有返回类型不一样,但是参数相同,这是不允许的,这里应该要看重载的定义。3.可以更改存取权限。

11.深入多态。有些类不应该被初始化,就像书中的Animal类,是一个抽象类,不能创建任何类型的实例,但是可以作为引用类型。(Animal dog = new Dog();)抽象类除了被继承过之外,是没有用途,没有值,没有目的的。(除了抽象的类可以由static成员之外)就算只有一个抽象方法,那么这个类也是抽象的。你必须实现所有的抽象方法,所有的意思是你继承下来的方法,是父类中的抽象方法,也可能是父类的父类中的抽象方法,但对于子类来讲,这些方法都被它继承了下来,所以子类应该实现这些抽象方法。

12.当某一个对象是以Object类型引用的时,Java会把它当作Object类型的实例,这也意味着你只能调用由Object类中声明的方法。例如下面代码:Object dog = new Dog();  dog.wow();//此时就会报错,因为dog此时就是一个Object对象,其中没有wow方法,他已经不是一个狗了!!!任何从ArrayList<Object>取出的东西都会被当作Object类型的引用而不管他原来是什么。编译器是根据引用类型来判断有哪些方法可以调用,而不是根据Object确定的类型来判断。解决一下上面的问题,难道真的不能将狗变回来吗,当然是可以的使用(Dog)dog这种形式。但是如果你不知道他的类型,如果运行中转换错误的话会抛出ClassCastException异常而终止。为避免这种异常,可以使用instanceof运算符来检查。例如下面代码:

if(dog instanceof Dog){
    Dog newDog = (Dog)dog;
}

 

13.不允许多重继承,会造成“致命方块”的问题。 如果Teacher执行run()方法,会发生什么?所以出现了接口。

14.书中有一句话为:注意到实现interface时还必须在某个类的继承之下。说的意思时只有继承才能实现interface接口?写了代码后发现不用继承也可以(可能这句话把每个类都继承Object也包括在内。)

public interface Pet {
    abstract void beFfiendly();
}
public class Test implements Pet{
    @Override
    public void beFfiendly() {
        System.out.println("1");
    }
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
    </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Test().beFfiendly();
}

}

 

15.父类中实现的接口子类也是可以继承的。extend只能有一个,但是implement可以由很多个。

16.要如何判断应该时设计类,子类,抽象类或接口呢?

  1.如果新的类无法对其他的类通过IS-A测试时,就涉及不继承其他类的类;

  2.只有在需要某类的特殊化版本时,以覆盖或增加新的方法来继承现有的类;

  3.当你需要定义一群子类的模板,又不想让程序员初始化此模板时,设计出抽象的类给他们用;

  4.如果你想定义出可以扮演的角色,使用接口;

 

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------第九章:对象的前世今生

1.对象的生存空间堆(heap),方法调用及变量的生存空间栈(stack)。当JVM启动时,他会从底层的操作系统取得一块内存,并以此区段来执行Java程序。栈:方法调用和局部变量;堆:所有的对象。

实例变量:被声明在类而不是方法里面,实例变量存在于所属的对象中,所以实例变量存在于对象所属的堆空间上。

局部变量:局部变量和方法的参数都是被声明在方法中,他们是暂时的,且生命周期只限于方法放在栈上这段时期。(也就是方法调用至执行完毕为止)

对象的引用变量和原始数据类型变量都是放在栈上的。

2.当调用一个方法时,该方法就会被放在调用栈的栈顶,如果栈顶的方法调用另一个方法,那么这个新调用的方法就会放在原来的方法之上。(非原始数据类型只是保存对象的引用而已,而不是对象的本身)如果局部变量是个对某一个对象的引用,那么只有变量本身放到栈上,对象本身指挥存在于堆上。

3.如果实例变量中存在对另一个对象的引用,那么在为该对象找空间时,除了对象中原始数据类型,还要存一个引用变量,而不是对象本身。如果只有声明没有赋值(Dog dog;),那么只会留下引用变量的空间;如果引用变量被赋值一个新的对象(new操作),这个对象才在堆上占有空间。

4.Dog dog = new Dog();对于这个代码,好像是在调用Dog()方法,但其实并不是,而是在调用构造函数。构造函数并不是方法,它带有new的时候会执行程序代码,换言之,这段程序代码会在你初始化一个对象时执行。唯一能调用构造函数的办法就是新建一个类(严格来讲,这是唯一在构造函数之外能够调用构造函数的方法),构造函数会在对象被赋值给引用之前就执行,因此我们就有了介入new的过程。

5.纠错:243页,括号中内容(实例变量由默认值),其实这个后续总结中改正了。

6.如果一个类有一个以上的构造函数,那么说明他们 也是重载的。如果你没有写构造函数,那么编译器才会调用无参的默认构造函数。实例变量是由默认值的,原始的默认值时0/0.0/false,引用的默认值时null;

7.构造函数是可以公有,私有或者不设定的,当为私有的时候,并不是代表完全不能存取,而是说该类之外不能存取。

 8.在创建新对象的时候,所有继承下来的构造函数都会被执行。抽象类也是有构造函数的,抽象的类不能执行new操作,但是抽象的类时父类,在子类创建出实例的时候还是会执行父类的构造函数。这是通过构造函数链(Constructor Chaining)来运行的。super()调用父类的无参构造函数,如果子类的构造函数中没有,也会隐式的写入一个super()方法,毕竟子类要继承父类的东西,所以这些东西应该先完成。

9.每一个子类的构造函数会立即调用父类的构造函数,如此一路上直找到Object函数。对super()的调用必须是构造函数中的第一个语句,不然的话会报错。

10.使用this()来从某个构造函数调用同一个类中的另一个构造函数,this()只能在构造函数中使用而且必须是第一条语句,super()和this()不能兼得,毕竟第一条语句只有一个。

11.类成员变量因为你在new一个对象时,分配了内存空间后会给所有的成员变量一次初始化,没有赋值的会给成员变量对应类型的“0”值,然后才构造对象初始化。每一个对象都有一个成员变量的副本,一直存在于对象的堆空间直到这个对象空间被回收。局部变量在用的时候是直接入栈的,如果没有赋值,这个变量就没有初始值,也就无法操作,所以局部变量要初始化。并且局部变量在方法结束后会出栈,就像它没有来过一样

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------第十章:数字和静态

1.Java中没有东西是全局的,但是我么可以让一种方法的行为不依靠实例变量的值(对象)。在Math这个类中的所有方法都不需要实例变量值(就是不需要创建对象),因为这些方法都是静态的,所以不需Math实例,你会用到的只有类本身。->引出static关键字。其可以标记出不需要类实例的方法。以类的名字来直接调用静态的方法(Math.max(1,2))。以引用变量的名称来调用非静态的方法。(就是使用对象来调用非静态方法。)构造函数标记为私有以后只有同一类的小恒徐才能调用。Math.round(-24.5)----->-24.

后看补充:

Duck d = new Duck();
String[] s = {};
d.main(s);
这段程序是合法的。如此调用的方法,也还是静态的。

 

2.静态方法不能调用非静态的变量,静态的方法也不能调用非静态的方法,静态的方法不知道堆上有哪些实例。

3.静态变量:它的值对所有的实例来说都相同。静态变量会在该类的任何对象创建之前就完成初始化,会在该类的任何静态方法执行之前就初始化。

4.静态的final变量是常数。常数变量的名称应该都是大写字母_静态变量的初始化方式,两种方式。如果不赋初值的话,是不会自动初始化的,会报错。

class Foo{  //注意静态final变量的命名规则类名下划线再加上名称
  public static final int FOO_X = 25; }

 

class Foo{  //采用静态初始化程序的方式
  public static final int FOO_x;
  static{
    FOO_X = 25;
  }
}

5.新知识,静态初始化程序:是一段在加载类时会执行的程序代码,他会在其他程序可以使用该类之前就执行,所以很适合放静态final变量的起始程序。

class Foo{
  final static int x;
  static{  //静态初始化程序
    x = 5;
  }
}

 

 6.final的其他用法:

//用在非静态变量上
class Foo{
  final int x = 5;
  final int whuffie;
  Foo(){
    whuffie = 5;  //whuffie的值不能够再发生改变了,值为5
  }
  void doStuff(final int x){}  //不能改变x的值
  void doMore(){
    final int x = 7;
  }
 }
final class MyLastClass{}

 

 

总结:final的变量代表你不能改变它的值;final的method代表你不能覆盖掉该method;final的类代表你不能继承该类(也就是创建他的子类)。(覆盖,重写父类中的方法,可以向前看)

7.非静态方法是可以调用该类的静态方法或者是静态的变量。final变量如果是静态的,要么定义时初始化,要么在静态代码块中初始化。final变量如果不是静态的,要么定义时初始化,要么在非静态代码块中初始化。

8.包装类自动包装和拆包。关于Integer初始化数值比较的文章。抄一部分:

1.Integer的值在-128到127时,JVM在运行时创建了一个缓存区域,并创建了一个integer的数组,这个数组存储了-128至127的值。因此会复用已有缓存立的值,也就是说,这个区间的Integer可以直接用等号进行判断。
2.Integer的值在-128到127之外时,Integer对象在堆上产生,不会复用已有缓存区的值,用等号会返回false。
3.Integer数值在和int类型比较时会自动拆箱转换成int类型,只要数值相等,无论Integer类型数值是否超过范围,使用==比较都会返回true。
3.Integer在使用new创建对象时,则两个对象即使值相等,在用==比较时也不相等,因为这两个值的对象地址不一样。建议使用equals()方法来比较Integer对象。
当使用Integer i = 150初始化时,会调用Integer的静态方法Integer.valueOf(int i)

 9.Stirng.format的使用:读文章。

10.Calender的使用:读文章。Calender是一个抽象类,不能直接通过new方法来获得其对象,但是我们可以通过Calender c = Calender.getInstance();获得一个具体子类的实例,这个方法更像是对静态方法的调用。

11.静态的import(尽量还是不要使用吧)。

 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------第十一章:异常处理

这一章之前了解的比较少,理解起来还是比较麻烦的。(先附上阿里java开发规范中关于异常的内容

 0.读文章 再读(有很多重复的地方,可以结合着看一下)

 

第十二章:看图说故事

看了两天,把我看得傻傻的……

就挑一些重点的来讲,

1.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);这个是点击❌就会退出程序的意思。如果没有这个的话即使你点击❌,程序依然没有停止。

2.frame是一个框架,组件不能直接放入框架中,应该放入frame的面板中-->frame.getContentPane().add(相应组件)。

3.然后一个重点就是如何获得用户的行为或者说是相关组件产生了什么事件。如果你想要知道按钮的时间,就会监听事件的接口。监听接口时介于监听(你)与事件源(例如按钮)之间的桥梁。下面是书上的一些话,让我有点悟了。

  事件来源是个可以将用户操作(点击鼠标,按键等)转换成时间的对象。事件源(例如按钮)会在用户做出相关动作的时候产生事件对象。要通过调用addActionListener(this)并传入ActionListener的引用(此例中就是你自己的这个程序,所以使用this,所以你这个程序应该implements相关的接口,并重写接口中的相关方法,例如actionPerformed())来向按钮注册。

  监听,事件源和事件之间的关系:

    监听获知事件(实现接口并向按钮注册);事件源发出事件(接受注册,取得用户事件,并在用户产生相关操作时调用监听的事件处理方法);事件对象携带事件信息(调用接口上的方法时把自己当作参数传进去,会把时间的信息带给监听者)

4.自己创建绘图组件真的运行不出来,我傻了。

5.剩下一个就是我感觉最重要的内部类,因为swing使用的已经很少了,重要的是理解其中的方法,例如内部类。内部类可以使用外部所有的方法与变量,就算是私有的也一样。

public class MyDrawPanel{  //这里就不用再使用接口了。
    JButton firstButton;
    JButton secondButton;
    public void go(){
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        firstButton = new JButton("first");
        secondButton = new JButton("second");
        firstButton.addActionListener(new FirstButtonListener());
        secondButton.addActionListener(new SecondButtonListener());
        frame.getContentPane().add(BorderLayout.CENTER,firstButton);
        frame.getContentPane().add(BorderLayout.EAST,secondButton);
        frame.setVisible(true);
        frame.setSize(200,200);
    }
    public static void main(String[] args) {
        new MyDrawPanel().go();
    }
    class FirstButtonListener implements ActionListener{  //第一个内部类
        @Override
        public void actionPerformed(ActionEvent e) {
            firstButton.setText("111");
        }
    }
    class SecondButtonListener implements ActionListener{  //第二个内部类
        @Override
        public void actionPerformed(ActionEvent e) {
            secondButton.setText("222");
        }
    }
}

 

内部类提供了在一个类中实现同一个接口的多次机会;任何时候你需要一个独立却又好像另一个类成员之一的类,内部类可能是唯一的解;内部类被用来当做某种外部类无法继承的子类( 即内部类可以实现多重继承 )

 第十三章:运用swing

布局管理器+组件

1.BorderLayout:分成五个区域,每一个区域只能放置一个组件,这是框架默认的布局管理器;

2.FlowLayout:每一个组件都会按照理想的大小呈现,并且会从左到右依照加入的顺序以可能会换行的方式排列,这是面板默认的布局管理器;

3.BoxLayout:以垂直的方向排列,不会自动换行,但是他会插入某种类似强制换行的机制来强制组件从新的一行开始排列。

这些布局管理器都比较好理解,不过书上关于JFrame的解释是真的可以:框架为什么不能像面板一样直接的加上相应的组件?JFrame会这么特殊是因为他是让事物显示在画面上的接点。因为Swing的组件纯粹由java构成,JFrame必须要链接到底层的操作系统以便来存取和显示装置。我们可以把面板想做是安置在JFrame上的纯java层,把JFrame想做是支撑面板的框架。

4.(中文是我自己理解着来翻译的)jTextField(可写文本框),JLabel(文本标签),JTextArea(可写文本域,可以加滚轮),JCheckBox(复选框),JList(多选文本框)。(翻译的有点憨憨的,后续把真的名字加上。) 

 

第十四章:序列化和文件输入输出

  看到标题的时候感觉序列化没怎么听过。

1.如果只有自己的java程序使用到了相关的数据,可以使用序列化;如果数据是需要被其他的程序使用,则写入到一个文本文件中。

2.序列化文件是一般人很难看懂的,但是他比纯文本文件更容易让程序恢复相关对象的状态。

3.将序列化的对象写入文件中:

FileOutputStream fileStream = new FileOutputStream("file.ser");
ObjectOutputStream os = new ObjectputStream(fileStream);
os.writeObject(objectName);
os.close();  //记得关闭

 

 上面这个程序写入的时候要处理相关类抛出的异常。

4.单一职责原则:每一个类只做一件事。对象写入,将对象序列化,ObjectOutputStream 连接到 FileOutputStream(对象被当作字节处理)->最后目的地(文件)

5.当对象被序列化的时候,被该对象引用的实例变量也会被序列化,且所有被引用的对象都会被序列化,这些操作都是自动的。

6.如果想要类能够被序列化,就要实现Serializable接口,这个接口没有任何需要实现的方法,他唯一作用就是声明实现他的类是可以被序列化的。整个对象的如果要被序列化,那他的所有包括引用变量所引用的对象也要可以被序列化。如果变量被标记为transient,则序列化可以跳过它。父类序列化以后,子类就不需要再实现相关的接口了。

7.解序列化:

FileInputStream fileName = new FileInputStream("file.ser");
ObjectInputStream os = new ObjectInputStream(fileName);
Object one = os.readObject();
ClassName newOne = (ClassName)one;  //类型转化
os.close();

 

 步骤:将文件中的二进制数据读入FileInputStream(对象被当作字节读入),将其和ObjectInputStream(加载类,加载实例变量)链接,将序列化的对象转化为正真的对象。

8.将字符串写入文本文件:

FileWriter writer = new FileWriter("Foo.txt");
writer.write("...");
writer.close();

 

9.关于File对象:

File f = new File("test.txt");    创建出File对象,本质上是一个地址,并不是里面的数据。

//创建目录
File dir = new File("name");
dir.mkdir();

//列出目录下的内容
if(dir.isDirectory()){
  String[] dirContents = dir.list();
  for(int i=0; i<dirContents.length; i++){
    System.out.println(dirContents[i]); 
  }
}

//获得文件的绝对路径
dir.getAbsolutePath()

//删除文件目录
f.delete()  //返回boolean

 

 10.缓冲区:字符串先被写入BufferedWriter缓冲区中,其链接到FileWriter(它会等到缓冲区满了以后一并写入file中。)

BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));  //写入
BufferedReader reader = new BufferedReader(new FileReader(fileName));  //读取(readLine()方法)

 

11.处理String的一个特殊方法split()。String[] strs = string.split(" ");  // 用空格将字符串隔开。

12.serialVersionUID:每当一个对象被序列化的时候,该对象都会被盖上一个类的版本识别ID。再还原对象的时候,jvm会比较对象和该类的serialVersionUID,如果版本不相符,则会抛出错误。如果你在存储后稍微改动一下原来的类,那么serialVersionUID就会不一样。即版本就会不相符。

 第十五章:网络与线程

  下面我只写了线程方面的东西,网络有关的下学期学校会讲,到时候补充。

 0. 首先感谢WMG-Eight对我解决我不知道的问题提出的帮助。(后面会说明相关的不理解)

1.线程是独立的线程,他代表独立的执行空间。Thread是一个表示线程的类,他有启动线程,连接线程和让线程闲置的方法。

2.对于这个过程的理解:java虚拟机调用main();main()启动新的线程,新的线程启动期间main()的线程会暂时停止执行;java虚拟机会在线程与原来的主线程之间切换直到两者都完成为止。

3.启动线程的一个做法:

Runnable threadJob = new MyRunnable();  //一个新的工作
Thread t = new Thread(threadJob);  //将工作交给工人
t.start();  //工人开始执行工作

 

 在这个中间,Runnable对象就像是一个工作,Thread对象更像是一个工人。

 第一步中,MyRunnable是一个实现Runnable接口的类,需要实现接口中的run()方法,run()方法就是线程启动以后需要执行的工作。Runnable只有一个方法,run();

4.线程的三个状态:可执行,执行中,堵塞(造成这个的原因可能是:等待其他线程的完成;等待被占用的对象;等待串流的数据)

5.写一段示例代码:

public class MyRunnable2 implements Runnable{
    @Override
    public void run() {
        go();
    }
    public void go(){
        doMore();
    }
    public void doMore(){
        System.out.println("111");
    }
}
class MyThread{
    public static void main(String[] args) {
        Runnable myrunnable = new MyRunnable2();
        Thread thread = new Thread(myrunnable);
        thread.start();
        System.out.println("000");
    }
}

 

6.等一会会加一篇关于线程的一篇文章(另外创造线程的两种方式)。Thread对象是不能重复使用的,一旦线程的run()方法完成之后,该线程就不能在重现启动。文章在这

7.ThreadName.sleep(num);让线程停止num毫秒。我们能够确定的就只是线程不会在指定时间内醒过来,但是线程也不一定会在时间过后就马上醒来变成执行中的状态,你只能确定他会回到可执行的状态。

8.两个线程可以完成同一个任务,既是将实现Runnable接口的MyRunnable对象传入至两个或多个线程中。Thread.currentThread()会返回当前线程对象。(Thread的实例对象有getName()方法);

9.并发性问题会引发竞争状态,竞争状态会引发数据的损毁。两个或两个以上的线程存取单一对象的数据时会发生这样的情况。

class BankAccount{
    private int banlance = 100;
    public int getBanlance(){
        return banlance;
    }
    public void withdrawl(int amout){
        banlance = banlance - amout;
    }
}
public class RyanAndMonicaJob implements Runnable{
    private BankAccount account = new BankAccount();
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
    Runnable ryanAndMonicaJob </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> RyanAndMonicaJob();
    Thread threadone </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Thread(ryanAndMonicaJob);
    Thread threadtwo </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Thread(ryanAndMonicaJob);
    threadone.setName(</span><span style="color: #800000;">"</span><span style="color: #800000;">Ryan</span><span style="color: #800000;">"</span><span style="color: #000000;">);
    threadtwo.setName(</span><span style="color: #800000;">"</span><span style="color: #800000;">Monica</span><span style="color: #800000;">"</span><span style="color: #000000;">);
    threadone.start();
    threadtwo.start();
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
    </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=<span style="color: #800080;">0</span>; i&lt;<span style="color: #800080;">10</span>; i++<span style="color: #000000;">){
        makeWithdrawl(</span><span style="color: #800080;">10</span><span style="color: #000000;">);
        </span><span style="color: #0000ff;">if</span>(account.getBanlance() &lt; <span style="color: #800080;">0</span><span style="color: #000000;">){
            System.</span><span style="color: #0000ff;">out</span>.println(<span style="color: #800000;">"</span><span style="color: #800000;">Overdrawn</span><span style="color: #800000;">"</span><span style="color: #000000;">);
        }
    }
}
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span> makeWithdrawl(<span style="color: #0000ff;">int</span><span style="color: #000000;"> amount){
    </span><span style="color: #0000ff;">if</span>(account.getBanlance() &gt;=<span style="color: #000000;"> amount){
        System.</span><span style="color: #0000ff;">out</span>.println(Thread.currentThread().getName()+<span style="color: #800000;">"</span><span style="color: #800000;"> is about to withdraw</span><span style="color: #800000;">"</span><span style="color: #000000;">);
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
            System.</span><span style="color: #0000ff;">out</span>.println(Thread.currentThread().getName()+<span style="color: #800000;">"</span><span style="color: #800000;"> is ready to sleep</span><span style="color: #800000;">"</span><span style="color: #000000;">);
            Thread.sleep(</span><span style="color: #800080;">500</span><span style="color: #000000;">);
        } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
            e.printStackTrace();
        }
        System.</span><span style="color: #0000ff;">out</span>.println(Thread.currentThread().getName()+<span style="color: #800000;">"</span><span style="color: #800000;"> woke up</span><span style="color: #800000;">"</span><span style="color: #000000;">);
        account.withdrawl(amount);
        System.</span><span style="color: #0000ff;">out</span>.println(Thread.currentThread().getName()+<span style="color: #800000;">"</span><span style="color: #800000;"> completes the withdrawl</span><span style="color: #800000;">"</span><span style="color: #000000;">);
    }
    </span><span style="color: #0000ff;">else</span><span style="color: #000000;">{
        System.</span><span style="color: #0000ff;">out</span>.println(<span style="color: #800000;">"</span><span style="color: #800000;">Sorry, not enough for </span><span style="color: #800000;">"</span>+<span style="color: #000000;">Thread.currentThread().getName());
    }

}

}

 

运行之后看输出,我们发现在一个线程“睡”的时候,另一个线程进入,检查钱数(这个钱数是正在睡的那个线程还没取之前的钱数),这样就有可能使得睡完之后的那个线程再去取钱会发现钱不够了这个情况。

(书中508页是一部分的输出,不要被迷惑了)

10.使用synchronized关键字来修饰方法使他每一次只能被单一的线程存取。就是让这个关键字修饰的区域成为最小操作原。

11.下面介绍使用对象的锁:每一个对象都有一个锁,对象的锁只会在有同步化方法的时候起作用。当对象有一个或多个同步化方法的时候,线程只有在取得对象锁的钥匙时才能进入同步化的方法中。锁是陪在对象上的,不是陪在方法上的。如果一个对象有两个同步化方法,就表示这两个线程无法进入同一个方法,也表示两个线程无法进入不同的方法。如果有多个方法会操作对象的实例变量,则这些方法都应该要有同步化的保护。当线程持有钥匙的时候,没有其他的线程可以进入该对象的同步化方法,因为每一个对象只有一个钥匙。

12.9中说了一种并行性引发的问题,下面是另一种并行性的问题:

public class TestSync implements Runnable{
    private int balance;
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            increment();
            System.out.println("balance is "+balance);
        }
    }
    public void increment(){
        int i = balance;  //出问题的点
        balance = i + 1;
    }
}

class TestSyncTest{
public static void main(String[] args) {
TestSync testSyncJob
= new TestSync();
Runnable target;
Thread a
= new Thread(testSyncJob);
Thread b
= new Thread(testSyncJob);
a.start();
b.start();
}
}

 

解释一下出问题的点:第一个线程进入increment()方法之后,执行i = balance,此时这个线程如果“睡”了,那么后一个线程就会将balance加一(进行完increment()方法后),然后第一个线程醒了以后又把balance变为balance+1(两个人虽然都做了这个,但是两个人加起来也只是将balance的值加了一个一而已)

13.死锁问题:因为两个线程互相持有对方正在等待的松溪,没有方法可以脱离这个情况,所以两个线程只好停下来等……(只要两个线程两个对象就可以引起死锁。

 第十六章:集合与泛型

数组的缺点:

  长度开始必须指定,指定后不能修改;

  保存的元素必须为同一种类型的元素(多态);

  使用数组进行增加/删除操作比较麻烦。

集合:

  动态的保存多个对象;

  提供一系列操作对象的方法;

  使用集合添加,删除元素的十一代码更加简洁。

 集合主要分为两种:单列集合,双列集合。

  Collection 接口下面有两个接口List 接口和Set 集合,他们的实现子类都是单列集合;

  Map 接口的实现子类是双列集合,存放方式为键值对的形式。

Collection接口:

public interface Collection<E> extends Iterable<E>

  Collection 实现子类可以存放多个元素,每一个元素都是Object;

  有些Collectio的实现类可以存放多个元素,有些不可以;

  有些Collection的实现类是有序的(List),有些不是有序的(Set);

  Collection接口没有直接实现类,是通过他的子接口Set和List来实现的。

//Collection的方法:
add()
remove()
contains()
size()
isEmpty()
clear()
addAll()
containsAll()
removeAll()

 ArrayList:

  默认容量大小10,添加元素之后才有默认容量,如果没有添加元素时,容量为零;elementData 存放元素的数组;size 实际的元素个数; 扩容原来的1.5倍

 HashSet的存储过程:

  1.根据hashcode计算保存的位置,如果此位置为空,则直接保存;如果不为空,再执行equals()方法,如果equals为true,则认为是重复的,否则则形成链表。

  31是一个质数,减少散列冲突;31可以提高执行效率

关于HashMap:初始容量为16,加载因子为0.75。

 

HansMap:线程不安全,运行效率快,允许用null作为key或者value;

Hashtable:线程安全,运行效率低,不允许使用null作为key或是value。

Properties:Hashtable的子类,要求key和value都是String。通常用于配置文件的读取。

TreeMap:实现了SortedMap接口,可以对key进行排序。

上面记录的比较简单,后续补充。

推荐:集合入门视频学习

  

 

 

 

 

 

 

  • 添加到短语集
     
    • 没有此单词集:中文(简体) -> 英语...
       
    • 创建新的单词集...
  • 拷贝
  • 添加到短语集
     
    • 没有此单词集:中文(简体) → 英语...
       
    • 创建新的单词集...
  • 拷贝
  • 添加到短语集
     
    • 没有此单词集:中文(简体) → 英语...
       
    • 创建新的单词集...
  • 拷贝
  • 添加到短语集
     
    • 没有此单词集:中文(简体) → 英语...
       
    • 创建新的单词集...
  • 拷贝

 

posted @ 2021-04-08 16:38  吃心王  阅读(340)  评论(0)    收藏  举报