-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
553 lines (553 loc) · 183 KB
/
search.xml
File metadata and controls
553 lines (553 loc) · 183 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[React-redux]]></title>
<url>%2F2020%2F05%2F14%2FReact-redux%2F</url>
<content type="text"><![CDATA[redux基础store是保存数据的地方。可以把它看成一个容器,整个应用只能有一个 Store。Redux 提供 createStore 这个函数,用来生成 Store。 import {createStore} from 'redux'; const store = createStore(reducer); store.getState() store 对象包含所有的数据,如果想得到某个时点的数据,就要对 Store 生成快照,这种时点的数据集合,就叫做 State, 当前时刻的 State,可以通过 store.getState() 拿到。 store.dispatch() 是 View 发出 Action 的唯一方法。结合 Action Creator store.dispatch(addTodo(“Learn Redux”)); store.subscribe(listener) 监听函数,一旦 State 发生变化,就会自动执行这个函数。在 React 项目中,就是把组件的 render 方法或 setState 方法放入 Listen,就会实现 View 的自动渲染了 actionaction 是一个对象,有一个必选 type 属性,其他属性自选: 作用是把 View 传进来的 state 改变之后返回一个新的 state 12345678910111213141516const action = { type: 'Add-Todo', payload: 'learn more'}``` 如果 View 要发生很多种类的消息,就会产生很多种 action,因此,我们可以定义一个函数来生成 Action,这个函数叫做 Action Creator。``` const ADD_TODO = '添加 TODO';function addTodo(text){ return { type: ADD_TODO, text }const action = addTodo('Learn Redux'); reducerStore 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生改变。这种 State 的计算过程叫做 Reducer是一个函数,接收 Action 和一个当前 State 作为参数,返回一个新的 State。 12345678910111213141516171819202122232425262728293031const reducer = (state, action) => { switch (action.type) { case '400': return state = action.payload; default: return state; }}``` reducer 函数的最重要特征是, 他是一个纯函数,也就是说,只要是同样的输入,必定得到同样的输出。纯函数是函数式编程的概念,必须遵守以下一些约束:1. 不得改写参数2. 不能调用系统 I/O 的 API3. 不能调用 Date.now() 或者 Math.random() 等不纯的方法,因为每次会得倒不一样的结果。由于 Reducer 是纯函数,就可以保证同样的 State,必定得到同样的 View, 但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象,请参考下面的写法。``` // state 是一个对象function reducer (state, action){ return Object.assign({}, state, {thingToChange}); // 或者 return {...state, ...newState };}// state 是一个数组function reducer(state, action){ return {...state, newItem};} 中间件(middleware)12345import {applyMiddleware, createStore} from 'redux';import createLogger from 'redux-logger';const logger = createLogger();const store = createStore(reducer, applyMiddleware(logger));const store = createStore(reducer, applyMiddleware(thunk, promise, logger)) 上面代码中,applyMiddleware 方法的三个参数,就是三个中间件,有的中间件有次序要求,比如,logger 一定放在最后,否则输出结果就会不正确。 React-Redux:React-Redux 将所有的组件分为两大类:UI 组件和容器组件UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。React-Redux 提供 connect 方法,用于从 UI 组件生成容器组件 12import { connect } from 'react-redux'const VisibleTodoList = connect()(TodoList); 上面代码中, TodoList 是 UI 组件,VisTodoList 就是由 React-Redux 自动生成的容器组件。但是,因为没有定义业务逻辑,丧母暗的这个容器的组件毫无意义,只是UI的一个单纯的包装层,为了定义业务逻辑,需要给出下面来那个方面的信息 输入逻辑: 外部的数据(即 state 对象)如何转换为 UI 组件的参数。 输出逻辑: 用户的动作如何变为 Action 对象,从 UI 组件传出去。 因此,connect 方法的完整 API 如下, 12345import {connect} from 'react-redux'const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps)(TodoList) 上面代码中,connect 方法接受两个参数:mapStateToProps 和 mapDispatchToProp, 他们定义了 UI 组件的业务逻辑,前者负责输入逻辑,后者负责输出逻辑。即将 state 映射到 UI 组件的参数 (props), 后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action. mapStateToProps()mapStateToProps 是一个函数,作用是建立一个(外部的)state 对象到(UI组件的)props 对象的映射关系。作为函数,mapStateToProps执行后应该返回一个对象,里面的每一个键值对就是一个映射。 12345const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) }} 上面代码中,mapStateToProps 是一个函数,它接受 state 作为参数,返回一个对象,这个对象有一个 todos 属性,代表 UI 组件的同名参数,后面的 getVisibleTodos 也是一个函数,可以从 state 算出 todos 的值。下面就是 getVisibleTodos 的一个例子,用来算出 todos。 123456789101112const getVisibleTodos = (todos, filter) => { switch (filter){ case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) default throw new Error('Unknown filter:' + filter) }} mapStateToProps 会订阅 Store, 每当 state 更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。mapStateToProps 的第一个参数总是 state 对象,还可以使用第二个参数,代表容器组件的 props 对象。 12345const mapStateToProps = (state, ownProps) => { return { active: ownProps.filter ===state.VisibilityFilter }} 使用 ownProps作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。connect 方法可以省略 mapStateToProps 参数,那样的话,UI组件就不会订阅 Store, 就是说 Store 的更新不会引起 UI 组件的更新。 mapDispatchToProps()mapDispatchProps 是 connect 函数的第二个参数,用来建立 UI 组件的参数到 Store.dispatch方法的映射,也就是说。它定义了哪些用户的操作应该当作Action, 它可以是一个函数,也可以是一个对象。如果mapDispatchProps是一个函数,会得倒 dispatch 和 ownProps (容器组件的props对象)两个参数。 12345678910const mapDispatchToProps = (dispatch, ownProps) => { return { onClick: () => { dispatch({ type: 'SET_VISIBILITY_FILTER', filter: ownProps.filter }) } }} 如果 mapDispatchToProps 是一个对象, 它的每个键名也是对应 UI 组件的同名参数,会被当作 Action creator,返回的 Action 会由 Redux 自动发出 123456const mapDispatchToProps = { onclick: (filter) => { type: 'SET_VISIBILITY_FILTER', filter: filter };} 组件connect 方法生成容器组件以后,需要让容器组件拿到 state 对象。才能生成 UI 组件的参数。React-Redux 提供 Provider 组件,可以让容器组件拿到 state。 12345678910111213import { Provider} from 'react-redux'import { createStore } from 'redux'import todoApp from './reducers'import App from './components/App'let store = createStore(todoApp);render( <Provider store = {store}> <App /> </Provider>, document.getElementById('root')) 上面代码中,Provider在根组件外面包了一层,这样一来,App 的所有制组件就默认拿到 state了。]]></content>
<categories>
<category>React</category>
</categories>
<tags>
<tag>redux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Cypress-常用函数]]></title>
<url>%2F2019%2F09%2F13%2FCypress-%E5%B8%B8%E7%94%A8%E5%87%BD%E6%95%B0%2F</url>
<content type="text"><![CDATA[Cypressprev() 获取当前元素的上一个兄弟元素123cy.get('.action-focus').focus() .should('have.class', 'focus') .prev().should('have.attr', 'style', 'color: orange;') submit() The subject must be a formcy.get('form').submit() next() 获取当前元素的下一个兄弟元素click() 单击一个元素 我们可以点击一个元素的9个特殊的位置 1234567891011 -----------------------------------| topLeft top topRight || || || || left center right || || || || bottomLeft bottom bottomRight | ----------------------------------- 我们可以接受一个 x 和 y 坐标 12345678cy.get('#action-canvas') .click(80, 75) // click 80px on x coord and 75px on y coord .click(170, 75) .click(80, 165) .click(100, 185) .click(125, 190) .click(150, 185) .click(170, 165) 可以通过 multiple: true 点击多个元素 cy.get('.action-labels>.label').click({ multiple: true }) dbclick() 双击一个元素cy.get('.action-div').dblclick().should('not.be.visible') check() 选中复选框或者单选框,并且可以接收单选或复选按钮的 value 参数1234cy.get('.action-multiple-checkboxes [type="checkbox"]') .check(['checkbox1', 'checkbox2']).should('be.checked')cy.get('.action-radios [type="radio"]') .check('radio1').should('be.checked') uncheck() 默认情况下,uncheck() 将取消所有匹配,也可接收一个 value 参数cy.get('.action-check [type="checkbox"]').check('checkbox1').uncheck('checkbox1').should('not.be.checked') select() 选择一个选项在下拉菜单的元素里,select() 使用文本匹配,也可使用 value 匹配cy.get('.action-select-multiple') .select(['apples', 'oranges', 'bananas']) cy.get('.action-select-multiple') .select(['fr-apples', 'fr-oranges', 'fr-bananas']) scrollIntoVIew() 将一个元素滚动到视图中,Cypress 知道向右和向下滚动cy.get('#scroll-both button').scrollIntoView().should('be.visible') scrollTO() 可以将窗口或者一个可滚动元素拖到一个明确的位置cy.scrollTo('bottom') // 如果直接和cy链式调用,将直接滚动整个窗口 cy.get('#scrollable-vertical').scrollTo(250, 250) // 滚动到一个明确的位置 cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' }) // 控制滚动的缓动 trigger() 触发一个Dom元素的事件its 获取前一个返回值的属性值invoke() 在前面生成的主题上调用方法12345cy.get('.trigger-input-range') .invoke('val', 25) .trigger('change') .get('input[type=range]').siblings('p') .should('have.text', '25'); as() 可以给一个路由或者 DOM 元素起别名first() 获取一组 DOM 元素的第一个元素1234567cy.get('.as-table') .find('tbody>tr').first() .find('td').first() .find('button').as('firstBtn'); // 在别名前面加上 @ cy.get('@firstBtn').click(); server() 打开一个服务器,将responses 路由到 cy.route() 和 cy.request().12cy.server();cy.route('GET', 'comments/*').as('getComment') should() 对当前的主题做一个断言判断当前主题的类名是success should('have.class', 'success') 多种方式检查文本元素 should('have.text', 'Colum content') should('contain', 'Colum content') should('have.html', 'Colum content') 检查元素是否获取到 should('match', 'td') 使用正则表达式进行文本内容匹配 .invoke('text') .should('match', /colum content/i) cy.contains 是一个更好的使用正则表达式匹配文本内容的方法 cy.get('.assertion-table').find('tbody tr:last') .contains('td', /column content/i).should('be.visible') should() 对当前的主题做一个断言判断当前主题的类名是success should('have.class', 'success') 多种方式检查文本元素 should('have.text', 'Colum content') should('contain', 'Colum content') should('have.html', 'Colum content') 检查元素是否获取到 should('match', 'td') 使用正则表达式进行文本内容匹配 .invoke('text') .should('match', /colum content/i) cy.contains() 是一个更好的使用正则表达式匹配文本内容的方法 cy.get('.assertion-table').find('tbody tr:last') .contains('td', /column content/i).should('be.visible') .and() 将多个断言链接在一起1234cy.get('.assertions-link') .should('have.class', 'active') .and('have.attr', 'href') .and('include', 'cypress.io') expect对指定的主题做 BDD 断言,使用expect 123456789describe('Explicit Assertions', () => { it("expect - make an assertion about a specified subject", () => { expect(true).to.be.true; const o = { foo: 'bar' }; expect(o).to.equal(o); expect(o).to.deep.equal({ foo: 'bar' }); expect('FooBar').to.match(/bar$/i); });}); assert对指定的主题进行TDD 断言,使用 assert 1234567it("assert - assert shape of an object", () => { const person={ name:'Joe', age:20, } assert.isObject(person,'value is object');}); 带有回调函数的 should如果包含的断言不够,我们可以使用 .should(cb) 函数写我们自己复杂的检查,将包含任意数量的显式断言的函数,传递给 should(), 回调函数将会被重试直到通过你的显示断言或者超时 1234567891011121314it('pass your own callback function to should()', () => { cy.get('.assertions-p') .find('p') .should(($p) => { const texts = $p.map((i, el) => Cypress.$(el).text()); const paragraphs = texts.get(); expect(paragraphs, 'has 3 paragraphs').to.have.length(3); expect(paragraphs, 'has expected text in each paragraph').to.deep.eq([ 'Some text from first p', 'More text from second p', 'And even more text from third p', ]); });}); each() 遍历一个元素数组123456it('.each() - iterate over an array of elements', () => { cy.get('.connectors-each-ul>li') .each(($el, index, $list) => { console.log($el, index, $list); });}); spread() 将一个数组作为一个单独的参数传给回调函数12345678910it('.spread() - spread an array as individual args to callback function', () => { // https://on.cypress.io/spread const arr = ['foo', 'bar', 'baz'] cy.wrap(arr).spread((foo, bar, baz) => { expect(foo).to.eq('foo'); expect(bar).to.eq('bar'); expect(baz).to.eq('baz'); });}); getCookie() 获得浏览器一个 cookie1234it('cy.getCookie() - get a browser cookie', () => { cy.get('#getCookie .set-a-cookie').click(); cy.getCookie('token').should('have.property', 'value', '123ABC');}) ## getCookies() 获得浏览器的所有 Cookie 12345678910it("cy.getCookie() - get a browser cookie", () => { cy.get('#getCookie .set-a-cookie').click(); cy.getCookies().should('have.length', 1).should((cookies) => { expect(cookies[0]).to.have.property('value', '123ABC'); expect(cookies[0]).to.have.property('httpOnly', false); expect(cookies[0]).to.have.property('secure', false); expect(cookies[0]).to.have.property('domain'); expect(cookies[0]).to.have.property('path'); })}) setCookie() 设置一个浏览器 Cookie12345it('cy.setCookie() - set a browser cookie', () => { cy.getCookies().should('be.empty'); cy.setCookie('foo', 'bar'); cy.getCookie('foo').should('have.property', 'value', 'bar'); }) clearCookie() 清除一个浏览器 Cookie1234567it('cy.clearCookie() - clear a browser cookie', () => { cy.getCookie('token').should('be.null'); cy.get('#clearCookie .set-a-cookie').click(); cy.getCookie('token').should('have.property', 'value', '123ABC'); cy.clearCookie('token').should('be.null'); cy.getCookie('token').should('be.null'); }); clearCookies() 清除浏览器的所有的 Cookie1234567it('cy.clearCookies() - clear browser cookies', () => { cy.getCookies().should('be.empty'); cy.get('#clearCookies .set-a-cookie').click(); cy.getCookies().should('have.length', 1); cy.clearCookies(); cy.getCookies().should('be.empty');}); children() 获得子 DOM 元素12345it('.children() - get child DOM elements', () => { cy.get('.traversal-breadcrumb') .children('.active') .should('contain', 'Data'); }); closest() 获得最近的祖先 DOM 元素12345it('.closest() - get closest ancestor DOM element', () => { cy.get('.traversal-badge') .closest('ul') .should('have.class', 'list-group') }) eq() 获取一个明确索引的DOM元素1234it('.eq() - get a DOM element at a specific index', () => { cy.get('.traversal-list>li') .eq(1).should('contain', 'siamese') }) filter() 获取特定选择器匹配的 DOM 元素1234it('.filter() - get DOM elements that match the selector', () => { cy.get('.traversal-nav>li') .filter('.active').should('contain', 'About')}) find() 获取选择器的后代 DOM 元素12345it('.find() - get descendant DOM elements of the selector', () => { cy.get('.traversal-pagination') .find('li').find('a') .should('have.length', 7)}) first() 获取第一个 DOM 元素1234it('.first() - get first DOM element', () => { cy.get('.traversal-table td') .first().should('contain', '1') }) last() 获取最后一个 DOM 元素1234it('.last() - get last DOM element', () => { cy.get('.traversal-buttons .btn') .last().should('contain', 'Submit')}) next() 获得下一个兄弟 DOM 元素1234it('.next() - get next sibling DOM element', () => { cy.get('.traversal-ul') .contains('apples').next().should('contain', 'oranges') }) ## nextAll() 获取该元素的所有下一个兄弟元素 12345it('.nextAll() - get all next sibling DOM elements', () => { cy.get('.traversal-next-all') .contains('oranges') .nextAll().should('have.length', 3) }) nextUntil() 获取该元素的所有下一个元素直到另一个元素为止1234it('.nextUntil() - get next sibling DOM elements until next el', () => { cy.get('#veggies') .nextUntil('#nuts').should('have.length', 3) }) not() 从元素集合中删除元素1234it('.not() - remove DOM elements from set of DOM elements', () => { cy.get('.traversal-disabled .btn') .not('[disabled]').should('not.contain', 'Disabled')}) parent() 获取上一级父 DOM 元素1234it('.parent() - get parent DOM element from DOM elements', () => { cy.get('.traversal-mark') .parent().should('contain', 'Morbi leo risus')}) parents() 从 DOM 元素中获取父 DOM 元素1234it('.parents() - get parent DOM elements from DOM elements', () => { cy.get('.traversal-cite') .parents().should('match', 'blockquote') }) parentsUntil() 从 DOM 元素获得父 DOM 元素,直到 el123456it('.parentsUntil() - get parent DOM elements from DOM elements until el', () => { cy.get('.clothes-nav') .find('.active') .parentsUntil('.clothes-nav') .should('have.length', 2) }) prev() 获得前一兄弟 DOM 元素1234it('.prev() - get previous sibling DOM element', () => { cy.get('.birds').find('.active') .prev().should('contain', 'Lorikeets') }) prevAll() 获得前面所有兄弟 DOM 元素1234it('.prevAll() - get all previous sibling DOM elements', () => { cy.get('.fruits-list').find('.third') .prevAll().should('have.length', 2) }) ## prevUntil() 获得前面的直到 el 的所有兄弟 DOM 元素 1234it('.prevUntil() - get all previous sibling DOM elements until el', () => { cy.get('.foods-list').find('#nuts') .prevUntil('#veggies').should('have.length', 3)}) siblings() 获得所有的兄弟 DOM 元素 (不包含本身元素)1234it('.siblings() - get all sibling DOM elements', () => { cy.get('.traversal-pills .active') .siblings().should('have.length', 2)})]]></content>
<categories>
<category>Cypress基础</category>
</categories>
<tags>
<tag>Cypress 基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TypeScript-AdvancedTypes]]></title>
<url>%2F2019%2F09%2F13%2FTypeScript-AdvancedTypes%2F</url>
<content type="text"><![CDATA[Advanced TypesIntersection Types(交叉类型)交叉类型是将多个类型合并为一个类型,这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性,例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。就是说这个类型的对象同时有了三种类型的成员。 123456789101112131415161718192021222324252627function extend<First, Second>(first: First, second: Second): First & Second { const result: Partial<First & Second> = {}; for (const prop in first) { if (first.hasOwnProperty(prop)) { (result as First)[prop] = first[prop]; } } for (const prop in second) { if (second.hasOwnProperty(prop)) { (result as Second)[prop] = second[prop]; } } return result as First & Second;}class Person { constructor(public name: string) { }}interface Loggable { log(name: string): void;}class ConsoleLogger implements Loggable { log(name:string) { console.log( `Hello, I'm ${name}.` ); }}const jim = extend(new Person('Jim'), ConsoleLogger.prototype);jim.log(jim.name); Union Types联合类型表示一个值可以是几种类型之一,我们用竖线(|)分隔每个类型,所以 number | string | boolean 表示一个值可以是 number,string,或 boolean。 1234567891011121314interface Bird { fly(); layEggs();}interface Fish { swim(); layEggs();}function getSmallPet(): Fish | Bird {}let pet = getSmallPet();pet.layEggs();pet.swim(); 如果我们有一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员 Type Guards and Differentiating Types(类型保护和区分类型)123456let pet = getSmallPet();if ((<Fish>pet).swim) { (<Fish>pet).swim;} else { (<Bird>pet).fly();} User-Defined type Guards这里可以注意到我们不得不多次使用类型断言,假若我们一旦检查过类型,就能在之后的每个分支里清楚地知道 pet 的类型就好了。TypeScript里的类保护机制让他成为了现实,类保护就是一些表达式,他们会在运行时检查以确保在某个作用域里的类型。要定义一个类型保护,我们只要简单的定义一个函数,它的返回值就是一个类型谓词。 123function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined;} 在这个例子里,pet is Fish 就是类型谓词,谓词为 parameterName is Type 这种形式,parameterName 必须是来自于当前函数签名里的一个参数名 每当使用一些变量调用 isFish 时,TypeScript 会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。 using the in operatorin 操作符现在充当类型的窄化表达式。 123456function move(pet: Fish | Bird) { if ("swim" in pet) { return pet.swim(); } return pet.fly();} typeof type guards 12345678910111213141516function isNumber(x: any): x is number { return typeof x === "number";}function isString(x: any): x is string { return typeof x === "string";}function padLeft(value: string, padding: string | number) { if (isNumber(padding)) { return Array(padding + 1).join("") + value; } if (isString(padding)) { return padding + value; } throw new Error( `Expected string or number,got'${padding}', ` )} 然而,必须要定义一个函数来判断类型是否是原始类型,这太痛苦了,幸运的是,现在我们不必将 typeof x === “number” 抽象成一个函数,因为 TypeScript 可以将它识别为一个类型保护,也就是说我们可以直接在代码里检查类型 123456789function padLeft(value: string, padding: string | number) { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } if (typeof padding === "string") { return padding + value } throw new Error( `Expected string or number,got'${padding}', ` )} 这些 typeof 类型保护只有两种形式被识别: typeof v ===”typename” and typeof v !== “typename”, “typename” 必须是”number”, “string”, “boolean”, or “symbol”,但是 TypeScript 并不会组织你与其他字符串比较,语言不会把那些识别为类型保护 instanceof type guardsinstanceof 类型保护是通过构造函数来细化类型的一种方式 12345678910111213141516171819202122232425interface Padder { getPaddingString(): string;}class SpaceRepeatingPadder implements Padder { constructor(private numSpaces: number) { } getPaddingString() { return Array(this.numSpaces + 1).join(" "); }}class StringPadder implements Padder { constructor(private value: string) { } getPaddingString() { return this.value; }}function getRandomPadder() { return Math.random() < 0.5 ? new SpaceRepeatingPadder(4) : new StringPadder(" ");}let padder: Padder = getRandomPadder();if (padder instanceof SpaceRepeatingPadder) { padder;}if (padder instanceof StringPadder) { padder;} instanceof 的右侧要求一个构造函数,TypeScript将细化为: 此构造函数的 prototype 属性的类型,如果它的类型不为 any 的话。 构造签名所返回的类型的联合。 Nullable typesTypeScript 具有两种哦就那个特殊的类型,null 和 undefined,他们分别具有值 null 和 undefined,默认情况下,类型检查器认为 null 与 undefined 可以赋值给任何类型。null 与 undefined 是所有其他类型的一个有效值。 1234let s = "foo";let sn: string | null = "bar";sn = null;sn = undefined;// Error]]></content>
<categories>
<category>TypeScript基础</category>
</categories>
<tags>
<tag>TypeScript基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TypeScript-Type Compatibility]]></title>
<url>%2F2019%2F09%2F13%2FTypeScript-TypeCompatibility%2F</url>
<content type="text"><![CDATA[Type CompatibilityTypeScript 中的类型兼容性基于结构化子类型,结构类型是一种仅基于其成员关联类型的方法,这与名义类型形成了对比 1234567891011interface Named { name: string;}class Person { name: string; constructor(name:string){ this.name=name; }}let p: Named;p = new Person(""); TypeScript 的类型系统允许某些在编译阶段无法确认其安全性的操作 Starting outTypeScript 结构化类型系统的基本规则是,如果 x 要兼容 y,那么 y 至少具有 x 相同的属性 123456interface Named { name: string;}let x: Named;let y = { name: "Alice", Location: "Seattle" };x = y; Comparing two functions1234let x = (a: number) => 0;let y = (b: number, s: string) => 0;y = x;x = y;// Error 要查看 x 是否能赋值给 y,首先看他们的参数列表,x 的每个参数必须能在 y 里找到对应类型的参数。注意的是参数的名字相同与否无所谓,只看他们的类型,这里,x 的每个参数在 y 中都能找到对应的参数,所以允许赋值。下面来看看如何让处理返回值类型,创建两个仅是返回值类型不同的函数。 1234let x = () => ({ name: "Alice" });let y = () => ({ name: "Alice", location: "Seattle" });x=y;y=x; // Error 类型系统强制原函数的返回值类型必须是目标函数返回值类型的子类型 Optional Parameters and Rest Parameters当比较函数兼容性的时候, 可选参数和必选参数是可互换的。源类型上有额外的可选参数不是错误,目标类型的可选参数在源类型里没有对应的参数也不是错误当一个函数有剩余参数时,它被当作无限个可选参数。有一个好的例子,常见的函数接受一个回调函数并用于对于程序员来说是可预知的参数但对类型系统来说是不确定的参数 1234function invokeLater(args: any[], callback: (...args: any[]) => void) {}invokeLater([1, 2], (x, y) => console.log(x + "," + y));invokeLater([1, 2], (x?, y?) => console.log(x + "," + y)); Functions with overloads对于有重载的函数,源函数的每个重载都要在目标函数上找到对应的函数签名,这确保了目标函数可以在所有源函数可调用的地方调用 Enums枚举类型与数字类型兼容,并且数字类型与枚举类型兼容,不同枚举之间是不兼容的 1234enum Status { Ready, Waiting };enum Color { Red, Blue, Green };let status = Status.Ready;status=Color.Green; // Error Classes类有静态部分和实例部分的类型. 当比较两个类类型的对象时, 只有实例成员会被比较,静态函数和构造函数不在比较的范围内 12345678910111213141516class Animal { feet: number; constructor(name: string, numFeet: number) { this.feet = numFeet; }}class Size { feet: number; constructor(numFeet: number) { this.feet = numFeet; }}let a: Animal;let s: Size;a = s;s = a; Private and protected members in classes类的私有成员和受保护成员会影响兼容性, 但检查类实例的兼容时,如果目标类型包含一个私有成员,如果目标类型包含一个私有成员,那么源类型必须包含来自同一个类的这个私有成员,同样地,这条规则也适用于包含受保护成员实例的类型检查,这允许子类赋值给父类,但是不能与具有相同形状的不同继承层次结构的类兼容。 ## Generics因为 TypeScript 是结构性的类型系统,类型参数只影响使用其作为类型一部分的结果类型。 123456interface Empty<T> {}let x: Empty<number>;let y: Empty<string>;x = y; 通过给该接口添加一个成员更改这个个例子,可以看到是如何工作的。 123456interface Empty<T> { data: T;}let x: Empty<number>;let y: Empty<string>;x = y; // Error 对于没指定泛型类型的泛型参数时,会把所有泛型参数当成 any 比较。然后用结果类型进行比较 1234567let identity = function <T>(x: T): T { return x;}let reverse = function <U>(y: U): U { return y;}identity = reverse;]]></content>
<categories>
<category>TypeScript基础</category>
</categories>
<tags>
<tag>TypeScript基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TypeScript-TypeInference]]></title>
<url>%2F2019%2F09%2F10%2FTypeScript-TypeInference%2F</url>
<content type="text"><![CDATA[Type InferenceBasicsTypeScript 里,在有些没有明确指出类型的地方,类型推论会帮助提供类型 let x = 3; Best common type当需要从几个表达式中推断类型的时候,会使用这些表达式的类型来推断出一个最合适的通用类型 let x = [0, 1, null]; 计算通用类型算法会考虑所有的候选类型,并给出一个兼容所有候选类型的类型 Contextual Typing上下文类型在很多情况下使用到,通常包含函数的参数,赋值表达式的右边,类型断言,对象成员和数组字面量和返回值语句,上下文类型也会做为最佳通用类型的候选类型 1234567class Animal{}class Rhino extends Animal{}class Elephant extends Animal{}class Snake extends Animal{}function createZoo():Animal[]{ return [new Rhino(), new Elephant(), new Snake()];}]]></content>
<categories>
<category>TypeScript基础</category>
</categories>
<tags>
<tag>TypeScript基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TypeScript-Enums]]></title>
<url>%2F2019%2F09%2F09%2FTypeScript-Enums%2F</url>
<content type="text"><![CDATA[Enums使用枚举我们可以定义一些带有名字的变量,使用枚举我们可以清晰地表达意图或者创建一组有区别的用例,TypeScript 支持数字的和基于字符串的枚举 Numeric enums可以使用 enum 关键字来定义一个 枚举 123456enum Direction { Up = 1, Down, Left, Right,} 我们可以完全不使用初始化器 123456enum Direction{ Up, Down, Left, Right,} 使用枚举很简单:通过枚举的属性来访问枚举成员,使用枚举的名字声明类型 1234567enum Responses { No, Yes,}function Respond(recipient:string,message:Responses):void{}Respond("Princess Caroline",Responses.Yes) String enums在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员来进行初始化123456enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT",} Heterogeneous enums(异构枚举)从技术角度来说,枚举可以混合字符串和数字成员 1234enum BooleanLikeHeterogeneousEnum { No = 0, Yes = "YES",} Computed and constant members每个枚举成员都有一个值,它可以是常量或就上出来的 它是枚举的第一个成员且没有初始化器,这种情况下他被赋值 0 1enum E { X } 它不带有初始化器且它之前的枚举成员是一个数字常量,这种情况下,当前枚举成员的值为他上一个枚举成员的值加 1 12345enum E1 { X, Y, Z }enum E2 { A = 1, B, C} 枚举成员使用常量枚举表达式初始化,常数枚举表达式是 TypeScript 表达式的子集,它可以在编译阶段求值,给一个表达式满足下面条件之一时,他就是一个常量枚举表达式: 一个枚举表达式字面量(主要是字符串字面量或数字字面量) 一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的) 带括号的常量枚举表达式 一元运算符 +, -, ~ 其中之一应用在了常量枚举表达式 常量枚举表达式作为二元运算符 +, -, *, /, %, <<, >>, >>>, &, |, ^ 的操作对象,若常数枚举表达式求值后卫 NaN 或 Infinity,则会在编译阶段报错所有其他情况的枚举成员被当作是需要计算得出的值1234567enum FileAccess { None, Read = 1 << 1, Write = 1 << 2, ReadWrite = Read | Write, G = "123".length} Union enums and enum member types存在一种特殊的非计算的常量枚举成员的子集:字面量枚举成员,字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为 任何字符串字面量(e.g. “foo”, “bar”, “baz”) 任何数字字面量(e.g. 1, 100) 应用了一元 - 符号的数字字面量(e.g. -1, -100) 当所有枚举成员都拥有字面量枚举值时,它就带有可以一种特殊的语义。 12345678910111213141516enum ShapeKind { Circle, Square,}interface Circle { kind: ShapeKind.Circle; radius: number}interface Square { kind: ShapeKind.Square sideLength: number;}let c: Circle = { kind: ShapeKind.Circle, radius: 100,} Enums at runtime枚举是在运行时真正存在的对象 1234567enum E { X, Y, Z}function f(obj: { X: number }) { return obj.X;}f(E); Enums at compile time12345678910111213enum LogLevel { ERROR, WARN, INFO, DEBUG}type LogLevelStrings = keyof typeof LogLevel;function printImportant(key: LogLevelStrings, message: string) { const num = LogLevel[key]; if (num <= LogLevel.WARN) { console.log('Log level kry is: ', key); console.log('Log level Value is: ', num); console.log('Log level message is: ', message); }}printImportant('ERROR', 'This is a message'); Reverse mappings除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了反向映射,从枚举值到枚举名字 12345enum Enum { A}let a = Enum.A;let nameOfA = Enum[a]; const enums1234567const enum Directions{ Up, Down, Left, Right,}let directions = [Directions.Up,Directions.Down,Directions.Left,Directions.Right] Ambient enums(外部枚举)Ambient enums 12345declare enum Enum { A = 1, B, C = 2}]]></content>
<categories>
<category>TypeScript基础</category>
</categories>
<tags>
<tag>TypeScript基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TypeScript-Generic]]></title>
<url>%2F2019%2F09%2F09%2FTypeScript-Generic%2F</url>
<content type="text"><![CDATA[Generics我们可以使用 any 类型来定义函数 12345function identity<T>(arg: T): T { return arg;};let output = identity("MyString");console.log(output); Generic function12345function identity<T>(arg: T): T { return arg;}let output = identity("MyString");console.log(output); Working with Generic Type Variables我们可以把泛型变量 T 当做类型的一部分使用,而不是整个类型,增加了灵活性 1234function loggingIdentity<T>(arg: T[]): T[] { console.log(arg.length); return arg;} Generic Types泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面,像函数声明一样 1234function identity<T>(arg: T): T { return arg; }let myIdentity: <T>(arg: T) => T = identity; 我们也可以使用不同的参数名,只要在数量上能对应上就可以。 1234function identity<T>(arg: T): T { return arg;}let myIdentity: <U>(arg: U) => U = identity; 我们也可以使用带有调用签名的对象字面量来定义泛型函数 1234function identity<T>(arg: T): T { return arg;}let myIdentity: { <U>(arg: U): U } = identity; 第一个泛型接口 1234567interface GenericIdentityFn { <T>(arg: T): T;}function identity<T>(arg: T): T { return arg; }let myIdentity: GenericIdentityFn = identity; 我们可能想把泛型参数当作整个接口的一个参数 1234567interface GenericIdentityFn<T> { (arg: T): T;}function identity<T>(arg: T): T { return arg;}let myIdentity: GenericIdentityFn<number> = identity; Generic Classes1234567class GenericNumber<T>{ zeroValue: T | undefined; add: ((x: T, y: T) => T) | undefined;}let myGenericNumber = new GenericNumber<number>(); myGenericNumber.zeroValue = 0; myGenericNumber.add = function (x, y) { return x + y; } Generic Constraints我们定义一个接口来描述约束条件,创建一个包含 .length 属性的接口,使用这个接口和 extends 关键字来实现约束。 1234567interface lengthwise { length: number;}function loggingIdentity<T extends lengthwise>(arg: T): T { console.log(arg.length); return arg;} Using Type Parameters in Generic Constraints12345function getProperty<T, K extends keyof T>(obj: T, key: K) { return obj[key];}let x = { a: 1, b: 2, c: 3, d: 4 }; getProperty(x, "a"); Using Class Types in Generics在 TypeScript 使用泛型创建工厂函数时,需要引用构造函数的类类型 123function create<T>(c: { new(): T; }): T { return new c();} A more advanced example 12345678910111213141516171819202122232425class BeeKeeper { hasMark: boolean;}class ZooKeeper { nameTag: number;}class Animal { numLegs: number;}class Bee extends Animal { keeper: BeeKeeper;}class Lion extends Animal { keeper: ZooKeeper;}function createInstance<A extends Animal>(c: new () => A): A { return new c();}createInstance(Lion).keeper.nameTag; createInstance(Bee).keeper.hasMark;]]></content>
<categories>
<category>TypeScript基础</category>
</categories>
<tags>
<tag>TypeScript基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TypeScript-Functions]]></title>
<url>%2F2019%2F09%2F09%2FTypeScript-Functions%2F</url>
<content type="text"><![CDATA[Functions两种 JavaScript 函数: 1234567891011// 方式一:function add(x, y) { return x + y;}let myAdd = function (x, y) { return x + y }; // 方式二:ley z = 100; function addToZ(x, y) { return x + y + z;} Function TypesTyping the Types让我们替上面那个函数添加类型 1234function add(x:number, y:number) { return x + y;}let myAdd = function (x:number, y:number) { return x + y }; Writing the function typelet myAdd : (x: number, y: number) => number = function (x: number, y: number): number { return x + y; }; 我们写参数类型像参数列表一样,给每个采纳数一个名称哥一个类型,这个名字只是为了提供可读性 let myAdd : (baseValue: number, Increment: number) => number = function (x: number, y: number): number { return x + y; }; Inferring the typeslet myAdd = function (x: number, y: number): number { return x + y; }; let myAdd: (baseValue: number, Increment: number) => number = function (x, y) { return x + y; }; Optional and Default ParametersTypeScript 里的每个函数都是必须的,传递给一个函数的参数个数必须与原函数期望的参数个数一致 Optional Parameters 123456function buildName(firstName: string, lastName?: string) { if(lastName){ return firstName + "" + lastName; }else return firstName;} 可选参数必须跟在必选参数后面 Default parameters 123456function buildName(firstName: string, lastName="Smith") { if(lastName){ return firstName + "" + lastName; }else return firstName; } 与普通的可选参数不同,带默认值的参数不需要放在必选参数的后面。如果带默认值的参数出现在必选参数前面,用户必须明确的传入 undefined 值来获得默认值 12345678function buildName(firstName="Will", lastName:string) { if(lastName){ return firstName + "" + lastName; }else return firstName;}let result = buildName(undefined, "Adms") Rest Parameters有时,我们想同时操作多个参数,或者,我们不知道会有多少参数传递进来,在 TypeScript 里,我们可以把所有的参数收集到一个变量里: 1234function buildName(firstName:string, ...restOfName:string[]){ return firstName+" "+restOfName.join(" ");}let employeeName = buildName("joseph","Samuel","Lucas","MacKinzie"); this12345678910111213141516let deck = { suits: ["hearts", "spades", "clubs", "diamonds"], cards: Array(52), createCardPicker: function () { return function () { let pickedCard = Math.floor(Math.random() * 52); let pickedSuit = Math.floor(pickedCard / 13); return { suit: this.suits[pickedSuit], card: pickedCard % 13 }; }; }}let cardPicker = deck.createCardPicker();let pickedCard = cardPicker();alert( `card: ${pickedCard.card} of ${pickedCard.suit}` ); 可以看到 CreateCardPicker 是个函数,并且它返回一个函数,如股票我们尝试运行这个程序,发现他没有弹出对话框而是报错了,因为 createCarPicker 返回的函数里的 this 设置成了 window 而不是 deck 对象,因为我们只是独立的调用了 cardPicker(),顶级的非方法调用会将 this 视为 window。为了解决这个问题,我们可以在函数被返回时就绑好正确的 this,这样的话,无论之后怎么使用它,都会引用绑定的 deck 对象,我们需要改变函数表达式来使用 ECMAScript 6箭头语法,箭头函数能保存函数时的 this 值,而不是调用时的值。 12345678910111213141516let deck = { suits: ["hearts", "spades", "clubs", "diamonds"], cards: Array(52), createCardPicker: function () { return () => { let pickedCard = Math.floor(Math.random() * 52); let pickedSuit = Math.floor(pickedCard / 13); return { suit: this.suits[pickedSuit], card: pickedCard % 13 }; }; }}let cardPicker = deck.createCardPicker();let pickedCard = cardPicker();alert( `card: ${pickedCard.card} of ${pickedCard.suit}` ); this parameters让我们往例子里添加有一些接口,Card 和 Deck,让类型的重用能够变得清晰简单些 12345678910111213141516171819202122232425interface Card { suit: string; card: number;}interface Deck { suits: string[]; cards: number[]; createCardPicker(this: Deck): () => Card;}let deck = { suits: ["hearts", "spades", "clubs", "diamonds"], cards: Array(52), createCardPicker: function (this: Deck) { return () => { let pickedCard = Math.floor(Math.random() * 52); let pickedSuit = Math.floor(pickedCard / 13); return { suit: this.suits[pickedSuit], card: pickedCard % 13 }; }; }}let cardPicker = deck.createCardPicker();let pickedCard = cardPicker();alert( `card: ${pickedCard.card} of ${pickedCard.suit}` ); 现在,TypeScript 知道 createCardPicker 期望在某个 deck 对象上调用,也就是说 this 是 Deck 类型的,而非 any this parameters in callbacks1234567891011interface UIElement { addClickListener(onclick: (this: void, e: Event) => void): void;}class Handler { info: string; onClickBad(this: Handler, e: Event) { console.log('clicked!'); }}let b = new Handler();uiElement.addClickListener(b.onClickBad); Overloads1234567891011121314151617let suits = ["hearts", "spades", "clubs", "diamonds"];function pickedCard(x: { suit: string; card: number }[]): number;function pickedCard(x: number): { suit: string; card: number; }function pickedCard(x: any): any { if (typeof x == "object") { let pickedCard = Math.floor(Math.random() * x % 13); return pickedCard; } else if (typeof x == "number") { let pickedSuit = Math.floor(x / 13); return { suit: suits[pickedSuit], card: x % 13 }; }}let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }]let pickedCard1 = myDeck[pickedCard(myDeck)];alert( `card: ${pickedCard1.card} of ${pickedCard1.suit}` );let pickedCard2 = pickedCard(15);alert( `card: ${pickedCard2.card} of ${pickedCard2.suit}` );]]></content>
<categories>
<category>TypeScript基础</category>
</categories>
<tags>
<tag>TypeScript基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TypeScript-Class]]></title>
<url>%2F2019%2F09%2F06%2FTypeScript-Class%2F</url>
<content type="text"><![CDATA[TypeScript-Class下面让我们看一个简单使用类的例子: Class1234567891011class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "hello, " + this.greeting; }}let greeter = new Greeter("world");console.log(greeter.greet()); Inheritance1234567891011121314class Animal{ move(distanceInMeters:number=0){ console.log( `Animal moved ${distanceInMeters}m` ); }}class Dog extends Animal{ bark(){ console.log("woof! woof! "); }}const dog=new Dog();dog.bark();dog.move(10);dog.bark(); 这个例子展示了最基本的继承功能 12345678910111213141516171819202122232425262728293031class Animal { name: string; constructor(theName: string) { this.name = theName; } move(distanceInMeters: Number = 0) { console.log( `${this.name}moved ${distanceInMeters}` ); }}class Snake extends Animal { constructor(name: string) { super(name); } move(distanceInMeters = 5) { console.log("Slithering..."); super.move(distanceInMeters); }}class Horse extends Animal { constructor(name: string) { super(name); } move(distanceInMeters = 45) { console.log("galloping..."); super.move(distanceInMeters); }}let sam = new Snake("sammy the Python");let tom: Animal = new Horse("Tommy the palomino");sam.move();tom.move(34); Public, private, and protected 修饰符在 TypeScript 中,每一个成员默认是 Public private当成员被标记成 private 时,它就不能在声明它的类的外部访问。当比较有 private 成员和 protected 成员的类型时,我们以不同的方式对待它们,如果两个类型中有一个是 private 成员,另一个成员必须有一个来自同一处声明的 private 成员,我们就认为这两个类型是兼容的,上述业适用于 protected 成员 protected 一个成员声明为 protected 可以被派生类访问, 构造函数也可以被声明为 protected,这意味值这个类不能在包含它的类外被实例化,但是能够被继承。 Readonly modifier我们可以使用 readonly 关键字将属性设置为只读的,只读属性必须在声明时或构造函数里被初始化。 12345678class Octopus { readonly name: string; readonly numberOfLegs: number = 8; constructor(theName: string) { this.name = theName; }}let dad = new Octopus("man with the 8 strong legs") Parameter properties参数属性允许我们在一个地方创建和初始化成员, 12345class Octopus{ readonly numberOfLegs:number=8; constructor(readonly name:string){}}let dad = new Octopus("man with the 8 strong legs") AccessorsTypeScript 支持 getters/setters 作为一种拦截对象成员访问的方法 123456789101112class Employee { private _fullName: string; get fullName(): string { return this._fullName; } set fullName(newName: string) { if (newName && newName.length > fullNameMaxLength) { throw new Error("fullName has a max length of " + fullNameMaxLength)'' } this._fullName = newName; }} Static Properties12345678910111213class Grid { static origin = { x: 0, y: 0 }; calculateDistanceFromOrigin(point: { x: number; y: number }) { let xDist = (point.x - Grid.origin.x); let yDist = (point.y - Grid.origin.y); return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale; } constructor(public scale: number) { }}let grid1 = new Grid(1.0);let grid2 = new Grid(5.0);console.log(grid1.calculateDistanceFromOrigin({ x: 10, y: 10 }));console.log(grid2.calculateDistanceFromOrigin({ x: 10, y: 100 })); Abstract Classes抽象类被视为其他派生类的基类使用,他们一般不会直接被实例化,不同于接口,抽象类可以包含成员的实现细节,abstract 关键字用于定义抽象类和在抽象类内部定义抽象方法 123456abstract class Animal { abstract makeSound(): void; move(): void { console.log("roaming the earth...";) }} Constructor functions当你在 TypeScript 声明一个类的时候,你实际上同时创建了多个声明,首先就是类的实例类型。 12345678910111213class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; }}let greeter: Greeter;greeter = new Greeter("world");console.log(greeter.greet());]]></content>
<categories>
<category>TypeScript基础</category>
</categories>
<tags>
<tag>TypeScript基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TypeScript - Basic Type]]></title>
<url>%2F2019%2F08%2F09%2FTypeScript-Basic%20Type%2F</url>
<content type="text"><![CDATA[Basic Types布尔值let isDone:boolean = false; 数字(和JavaScript一样,TypeScript里的所有数字都是浮点型的,这些浮点类型是number) 1234let decLiteral: number = 6;let hexLiteral: number = 0Xf00d;let binaryLiteral: number = 0b1010;let octaliteral: number = 0o744; 字符串(使用单引号或者双引号表示字符串) 12let name; string = " bob";name = "smith"; 可以使用模板字符串,也可以定义多行文本和内嵌表达式,这种字符串是被反引号包围( ` ), 并且以$( expr )这种形式嵌入表达式 1234let name: string = `Gene` ;let age: number = 37;let sentence: string = `Hello, my name is ${ name }.I 'll be ${ age+1 } years old next month.`; 数组在元素类型后面接上[], 表示此类型组成的一个数组: let list: number[] = {1, 2, 3}; 使用数据泛型,Array<元素类型> let list: Array< number > = {1, 2, 3}; 元组 Tuple(允许表示一个已知元素数量和类型的数组,各元素的类型不必相同) 123456//Declare a tuple typelet x: [string , number];//Initializex = ["hello", 10];//OK//Initialize it incorrectlyx = [10,'hello'];//Error 当访问一个已知索引的元素,会得到正确的类型: 12console.log( x[0].substr(1));// OKconsole.log( x[1].substr(1));// Error,'numbers' does not have 'substr' 当访问一个越界的元素,会使用联合类型替代:(联合类型是高级主题) 123x[3] = "world"; // OK,字符串可以赋值给(string | sumber)类型console.log(x[5].toString());//OK,'string' 和'number'都有toStringx[6] =true ;//Error,布尔不是(string | number)类型 枚举12enum Color { Red, Green,Blue}let c: Coloe = Coleo.Green; 默认情况下,从0开始为元素编号,也可以手动设置 Any(使类型检查器不对这些值检查直接让他们通过编译阶段) 123let notSure: any = 4; notSure = "maybe a string instead"; notSure = false; any 类型是十分有用的,它允许你在编译时可选择的包含或者移除类型检查,你可能认为在其他语言中 Object 有类似的作用,然而,Object只允许你给它赋任意值,即便它拥有方法,你不能调用。 123let notSure: any = 4;notSure.ifitExists();//okay, ifitExists might exist at runtimenotSure.toFixed();//Okay, toFixed exists 12let prettySure: Object = 4;prettySure.ToFixed();//Error:Property "toFind" doesn't exist on type 'Object'. 如果你只知道一部分数据类型的时候,any 类型也是有用的,比如你可能有一个包含了不同数据类型的数组 12let list: any[] = [1, true, "free"];list[1] = 100; void某种程度上来说,void 类型与 any 类型相反,它表示没有任何类型,你通常会看到当一个函数没有返回值的时候,该函数的返回值类型是 void 123function warnUser(): void { console.log("This is my warning message");} 声明一个 void 类型的变量没有什么大用,应为你只能为它赋予 undefined 和 null: 12let unusable: void = undefined;let unKnow: void = null; Null and UnderfinedTypeScript 里,undefined 和 null 两者各自有自己的类型分别叫做 undefined 和 null,和 void 相似,他们的本身用处不是很大: 12let u: undefined = undefined;let n: null = null; 默认情况下,null 和 undefined 是所有类型的子类型,这意味着你可以将 null 和 undefined 赋值给任何类型就像 number。然而,你指定了 –strictNullChecks 标记,null 和 undefined 只能赋值给他们各自本身,(其中 undefined 也不能赋值给 void),这就帮助避免了很多常见的问题,也许你想在某处传一个 string 或 null 或 undefined,你可以使用联合类型 string | null | undefined。注意:我们鼓励尽可能多的使用 –strictNullChecks Nevernever 类型表示的是那些永不存在的类型。例如,never 类型是那些总是会抛出异常或者根本就不会有返回值的函数表达式或者箭头函数的返回值类型,当变量被永不为真的类型保护所约束时,该变量可能是 never 类型。 never 类型是任何类型的子类型,也可以赋值给任何类型,除了 never 本身以外,没有值可以赋值给 never 类型,即便是 any 也不可以赋值给 never。 Objectobject 表示的是除 number, string, boolean, symbol, null, undefined 的非原始类型,使用 object 类型,就可以更好的表示像 object.create 这样的 API。例如 1234567declare function create(o: object | null): void;create({ prop: 0 });OKcreate(null);OKcreate(42);//Errorcreate("string");//Errorcreate(false);//errorcreate(undefined);//Error Type assertions 类型断言有两种形式,第一种是尖括号语法: 12let someValue: any = "this is a string ";let strLength: number = (<string>someValue).length; as 语法 12let somevalue: any="this is a string"; let strLength: number = (someValue as string).length; 当你在 TypeScript 里使用 JSX 时,只有 as 语法断言是被允许的]]></content>
<categories>
<category>TypeScript基础</category>
</categories>
<tags>
<tag>TypeScript基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-浅度复制与深度复制]]></title>
<url>%2F2019%2F08%2F06%2FC-%E6%B5%85%E5%BA%A6%E5%A4%8D%E5%88%B6%E4%B8%8E%E6%B7%B1%E5%BA%A6%E5%A4%8D%E5%88%B6%2F</url>
<content type="text"><![CDATA[浅度复制和深度复制浅度复制浅度复制指按照引用复制对象简单的按照成员复制对象可以通过派生于System. Object的MemberwiseClone()方法来完成,这是一个受保护的方法,这个方法提供的复制功能称为浅度复制 12345678910public class Content { public int val;}public class Cloner { public Content MyContent = new Content(); public Cloner(int newVal) => MyContent.val = newVal; public object GetCopy() => MemberwiseClone();} 123456Cloner mySource = new Cloner(5);Cloner myTarget = (Cloner) mySource.GetCopy();Console.WriteLine($ "myTarget.MyContent.Val={myTarget.MyContent.val}");mySource.MyContent.val = 2;Console.WriteLine($ "myTarget.MyContent.Val={myTarget.MyContent.val}");Console.ReadKey(); 深度复制复制的是一个对象的值可以实现一个ICloneable接口,以标准方式进行深度复制,如果使用这个接口,就必须实现它包含的Clone()方法,这个方法返回一个System. Object的值,我们可以采用各种处理方式,实现所选的任何一个方法体来得到这个对象,如果愿意就可以进行深度复制,但不是必须的。 12345678910111213public class Cloner: ICloneable { public Content MyContent = new Content(); public Cloner(int newVal) => MyContent.val = newVal; public object Clone() { Cloner clonedCloner = new Cloner(MyContent.val); return clonedCloner; } //public object GetCopy() => MemberwiseClone();} 注意在比较复杂的对象系统中,调用Clone是一个递归过程 12345678910public class Cloner: ICloneable { public Content MyContent = new Content(); public object Clone() { Cloner clonedCloner = new Cloner(); clonedCloner.MyContent = MyContent.Clone(); return clonedCloner; }}]]></content>
<categories>
<category>C#基础</category>
</categories>
<tags>
<tag>C#基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-迭代器]]></title>
<url>%2F2019%2F08%2F06%2FC-%E8%BF%AD%E4%BB%A3%E5%99%A8%2F</url>
<content type="text"><![CDATA[迭代器只要实现了IEnumerable接口,就可以使用foreach循环在foreach循环中,迭代一个collectionObject集合的过程如下: 调用collectionObject. GetEnumerator(), 返回一个IEnumerator引用,这个方法可通过IEnumerable接口的实现代码来获得,但这是可选的。 调用所返回的IEnumerator接口的MoveNext()方法。 如果MoveNext() 方法返回true,就使用IEnumerator接口的Current属性来获取对象的一个引用,用于foreach循环 重复前面两步,直到MoveNext()方法返回false为止,此时循环停止 IEnumerable和IEnumerator使用场景: 如果要迭代一个类,则使用方法GetEnumerator(), 其返回类型是IEnumerator。 如果要迭代一个类成员,例如一个方法,则使用IEnumerable。 在迭代器块中,使用yield关键字选择要在foreach循环中使用的值yield return < value >; 1234567891011121314151617public class Program { public static IEnumerable SimpleList() { yield return "string 1"; yield return "string 2"; yield return "string 3"; } static void Main(string[] args) { foreach(var x in SimpleList()) { Console.WriteLine(x); } Console.ReadKey(); }} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849public class Program { private long min; private long max; public Program(): this(2, 100) {} public Program(long minimum, long maxinum) { if (minimum < 2) { min = 2; } else { min = minimum; } max = maxinum; } public IEnumerator GetEnumerator() { for (var possiblePrime = min; possiblePrime <= max; possiblePrime++) { bool isPrime = true; for (var possibleFactor = 2; possibleFactor <= (long) Math.Floor(Math.Sqrt(possiblePrime)); possibleFactor++) { var remainderAfterDivision = possiblePrime % possibleFactor; if (remainderAfterDivision == 0) { isPrime = false; break; } } if (isPrime) { yield return possiblePrime; } } } static void Main(string[] args) { var primeFrom2to1000 = new Program(55, 100); foreach(long i in primeFrom2to1000) { Console.Write($ "{i} "); } Console.ReadKey(); }} 迭代器和集合在字典类型的集合中的对象使用迭代器进行迭代存储 12345678910111213141516171819202122public class Animal: DictionaryBase { public void Add(string newID, Animal newAnimal) { Dictionary.Add(newID, newAnimal); } public void Delete(string animalID) { Dictionary.Remove(animalID); } public Animal this[string animalID] //声明索引器 { get { return (Animal) Dictionary[animalID]; } set { Dictionary[animalID] = value; } }} 可在这段代码中添加如下的简单迭代器,以便执行预期操作(对子类特有的操作)://不加此方法,程序在编译的时候可以编译过去,但是会在运行时报错 12345678public new IEnumerator GetEnumerator() { foreach(object animal in Dictionary.Values) { yield return (Animal) animal; }} 现在可使用下面的代码来迭代集合中的Animal对象: 123456789101112var animal1 = new Animal();animal1.Add("01", new Animal() { Name = "dog"});animal1.Add("02", new Animal());animal1.Add("03", new Animal());animal1.Add("04", new Animal());foreach(Animal a in animal1) { Console.WriteLine(a.Name);}]]></content>
<categories>
<category>C#高级</category>
</categories>
<tags>
<tag>C#高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-特性和预编译指令]]></title>
<url>%2F2019%2F08%2F06%2FC-%E7%89%B9%E6%80%A7%E5%92%8C%E9%A2%84%E7%BC%96%E8%AF%91%E6%8C%87%E4%BB%A4%2F</url>
<content type="text"><![CDATA[特性(Attribute)特性(Attribute) 是用于在运行时传递程序中各种元素(比如类,方法,结构,枚举,组件等)的行为信息的声明性标签,. NET框架提供了两种类型的特性:预定义特性和自定义特性. NEt提供了三种预定义特性: AttributeUsage [AttributeUsage( validon, AllowMultiple=allowmultiple, Inherited=inherited)]validon 规定特性可被放置的语言元素,它是枚举器AttributeTargets的值的组合,默认值是AttributeTarget. Allallowmultiple(可选)为该特性的AllowMultiple属性(property)提供一个布尔值,表示在一个地方能否被多次使用inherited(可选的)为该特性的提供一个布尔值,表示该特性可否被派生类所继承 Conditional 标记了一个条件方法,它会引起方法调用的条件编辑,取决于指定的值,比如Debug和Trace[Conditional( conditionalSymbol)]使用Conditional是封闭#if和#endif内部方法的替代方法条件方法受到以下限制 -条件方法必须是类声明或结构声明中的方法,如果在接口声明中的方法上指定Conditional属性,将出现编译时错误 -条件方法必须有返回值 -不能用override修饰符标记条件方法,凡是,可以用virtual修饰符标记条件方法 -条件方法不能是接口方法的实现 -如果条件方法用在“委托创建表达式”中,也会发生编译时错误 Obsolete [Obsolete(" 方法提示 ", bool )]//标注方法为过期方法, bool 表示调用以后编译器是否报错,true 表示报错。 C#中的预编译指令 #define 和#undef ` #define DEBUG告诉编译器存在给定名称的符号,类似于声明一个变量,这个变量没有真正的值,只是存在而已,这个符号不是实际代码的一部分,而只在编译器编译黛玛诗存在,在C#中没有任何意义 #undef DEBUG`如果符号不存在,#undef没有任何作用,如果符号已存在,则#define也不起作用,必须把#define和#undef命令放在C#源文件的开头为止,在声明要编译的任何对象的代码之前,#define本身没有什么用,但与其他预处理指令(特别是#if)结合使用时,它的功能就非常强大了。 #if、#elif、#else 和#endif 这些指令告诉编译器是否要编译某个代码块。 12345678int DoSomeWork(double x) { # if DEBUG Console.WriteLine("x is " + x);# endif} 这段代码只有前面的#define命令定义了符号DEBUG后才执行 #warning 和 #error 当编译器遇到他们时,会分别产生警告或错误。如果编译器遇到#warning指令,会给用户显示#warnong指令后面的文本,之后编译继续执行,如果编译器遇到#error指令,就会给用户显示后面的文本,作为一条编译错误消息,然后立即退出编译,不会产生IL代码 #region 和#endregion 用于吧一段代码标记为有给定名称的一个快 #line 改变编译器在警告个错误信息中显示的文件名和行号信息 #pragma 抑制或还原指定的编译警告,可以在累活方法级别执行]]></content>
<categories>
<category>C#高级</category>
</categories>
<tags>
<tag>C#高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-反射]]></title>
<url>%2F2019%2F08%2F06%2FC-%E5%8F%8D%E5%B0%84%2F</url>
<content type="text"><![CDATA[反射什么是元数据,什么是反射? 程序是用来处理数据的,文本和特性都是数据数据,而我们程序本身(类的定义和BCL中的类)这些都是数据 有关程序及其类型的数据被称为元数据(metadata), 他们保存在程序的程序集中。 程序在运行时,可以查看其它程序集或其本身的元数据。一个运行的程序查看本身的元数据或者其它程序集的元数据的行为叫做反射 TypeBCL 声明了一个叫做Type的抽象类,它被设计用来包含类型的特性,使用这个类的对象能让我们获取程序使用的类型的信息。 由于Type是抽象类,因此不能利用它去实例化对象, 对于程序中用到的每一个类型,CLR都会创建一个包含这个类型信息的Type类型的对象 程序中用到的每一个类型都会关联到独立的Type类的对象 不管创建的类型有多少个示例,只有一个Type对象会关联到所有这些实例 获取Type对象 通过类的实例来获取Type对象 Type t = myInstance. GetType()在object类有一个GetType的方法,返回Type对象,因为所有类都是从object继承的,所以我们可以在任何类型上使用GetType()来获取它的Type对象 通过typeof 运算符合类名获取Type对象 Type t= typeof(ClassName) 获取里面的字段12345FieldInfo() fi = t.GetFields(); //只能获取public字段foreach(FieldInfo f in fi) { Console.WriteLine(f.Name + " ");} 获取里面的属性12345PropertyInfo[] fi = t.GetProperties(); //只能获取public字段foreach(PropertyInfo f in fi) { Console.WriteLine(f.Name + " ");} 获取里面的方法12345MethodInfo[] fi = t.GetMethods(); //只能获取public字段foreach(MethodInfo f in fi) { Console.WriteLine(f.Name + " ");} 获取Assembly对象12MyClass my = new MyClass();Assenbly assem = my.GetType().Assembly; //通过累的type对象获取他所在的程序集Assembly]]></content>
<categories>
<category>C#高级</category>
</categories>
<tags>
<tag>C#高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-static关键字]]></title>
<url>%2F2019%2F08%2F05%2FC-static%E5%85%B3%E9%94%AE%E5%AD%97%2F</url>
<content type="text"><![CDATA[静态类静态类与非静态类的重要区别在于静态类不能被shi’li’hu]]></content>
<categories>
<category>C#基础</category>
</categories>
<tags>
<tag>C#基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-XML]]></title>
<url>%2F2019%2F08%2F04%2FC-XML%2F</url>
<content type="text"><![CDATA[XMLXML基础 XML是一种标记语言,与HTML很类似 XML的设计宗旨是持久化或传递数据,而非显示数据 XML标签没有被预定义,我们可以根据需要自行定义标签,具有自我描述性 XML本质上仅仅是才纯文本 XML模式XML文档可以用模式来描述,模式是另一个XML文件,描述了允许在一个特定的文档中使用的元素和特性 XML文档对象模型常用的DOM类:XmlNode: 这个类表示文档树中的一个节点,是许多类的基类,如果这个节点表示XML文档的根,就可以从它导航到文档的任意位置XmlDocument: 扩展了XmlNode类,但通常是使用XML的第一个对象,因为这个类用于加载磁盘或其他地方的数据并在这些位置保存数据XmlElement: 表示XML文档中的一个元素,XmlElement派生于XmlLinkedNode,XmlLinkedNode派生于XmlNodeXmlAttribute: 表示一个特性,与XmlDocument类一样,派生于XmlNode类XmlText:表示开始标记和结束标记之间的文本XmlNodeList: 标识一个节点集合 XmlDocument12var document = new XmlDocument();document.Load(@"C:\地址");、//文件名是一个绝对路径 XmlElement属性 FirstChild该属性返回当前节点之后的第一个子节点 LastChild返回当前节点之后的最后一个子节点 ParentNode返回当前节点的父节点 NextSibling返回有相同父节点的下一个节点 HasChildNodes检查当前元素是否有子元素,而不是必须获取FirstChild的值并检查是否为null 获取节点值 InnerText 获取当前节点中所有子节点的文本,把它作为一个串联字符串返回 InnerXml 返回类似于InnerText的文本 Value Value属性是操作文档中信息的最精炼方式 创建节点 CreateNode创建任意类型的节点,该方法有三个重载,两个允许创建XmlNodeType 枚举中所列出的类型,另一个允许把要使用的节点类型指定为字符串, CreateElement只能创建XmlElement类型的节点 CreateAttribute只能创建XmlAttribute类型的节点 CreateTextNode创建XmlTextNode类型的节点 CreatCommen创建注释 插入节点 AppendChild把一个节点追加到XmlNode类型或派生类型的节点上 InsertAfter可以控制插入新节点的位置,该方法有两个参数,第一个是新节点,第二个是在其后插入新节点的节点 InsertBefour新节点在参考节点之后 删除节点 RemoveAll删除节点之上的所有子节点,并且删除字典的特性 RemoveChild删除节点上的一个子节点,返回从文档中删除的节点 选择节点 SelectSingleNode选择一个节点 SelectNodes以XmlNodeList类的形式返回一个节点集合 XML转换为JSONC# 系统库有限的支持JSON. 但是可以使用免费的第三方类库将XMl转换为JSON.]]></content>
<categories>
<category>C#高级</category>
</categories>
<tags>
<tag>C#高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[GitHub使用技巧]]></title>
<url>%2F2019%2F08%2F04%2FGitHub%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7%2F</url>
<content type="text"><![CDATA[GitGit两大特点 版本控制 分布式 git最基本操作初始化仓库:git init将一个文件添加到暂存区git add code.txt创建一个版本记录git commit -m 'verson1'(版本的说明信息)查看版本记录git log版本回退git reset --hard head^ /hard head~1-100再次返回git reset --hard 版本号查看之前的操作记录git reflog查看当前文件状态git status未添加到暂存区之前撤销改动(增删改)git checkout -- 文件名撤销暂存区的修改git reset HEAD file对比文件不同 工作区文件与版本文件对比 git diff HEAD – code.txt–代表版本文件++代表工作区文件 对比两个版本之间某个文件的不同 git diff HEAD HEAD^ – code.txt 删除文件 不放在暂存区 rm 文件名 放在暂存区 git rm 文件名 git分支操作查看分支git branch创建分支git branch 分支名创建并切换一个新的分支git checkout -b 分支名切换分支git checkout master合并git merge 分支名删除分支git branch -d 分支名合并冲突(两个分支上同时提交且编辑同一个文件) 每次操作暂存区内容都必须git commit 创建版本记录 工作区(Working Directory)电脑中的目录,就是一个工作区 版本库(Repository)工作区有一个隐藏目录.git, 这个不是工作区,而是git的版本库git的版本库里存了很多东西,其中最重要的就是称为stage的暂存区,还有git为我们自动创建的第一个分支master,以及指向master的一个指针HEAD]]></content>
<categories>
<category>GitHub</category>
</categories>
<tags>
<tag>其他</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-Lambda表达式]]></title>
<url>%2F2019%2F08%2F04%2FC-Lambda%E8%A1%A8%E8%BE%BE%E5%BC%8F%2F</url>
<content type="text"><![CDATA[Lambda表达式“Lambda表达式”是一个匿名函数,是一种高效的类似于函数式编程的表达式,Lambda简化了了开发中需要编写的代码量,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式。lambda表达式使用=>运算符,该运算符读作”goes to”。Lambda运算符的左边是输入参数,右边是表达式或语句块。x => x*x可以将此表达式分配给委托类型123456delegate int del(int i);static void Main(string[] args){ del myDelegate = x => x * x; int j = myDelegate(5); //j = 25} 目录树类型123456789101112using System.Linq.Expressions;namespace ConsoleApplication1{ class Program { static void Main(string[] args) { Expression<del> myET = x => x * x; } }} 表达式位于=>运算符右侧的lambda表达式称为”表达式lambada”,表达式Lambda会返回表达式的结果,并采用以下基本形式(input parameters) => expression仅当lambda只有一个输入参数时,括号才是可选的;否则括号是必须的,括号内的两个或更多个输入参数使用逗号加以分隔;(x, y) => x == y有时,编译器难以或无法推断输入类型。如果出现这种情况,你可以显示指定类型(int x, string s) => s.Length > x使用空括号指定零个输入参数:() => SomeMethod()当Lambada表达式中有多个语句时(input paramenters) => {statement;}]]></content>
<categories>
<category>C#高级</category>
</categories>
<tags>
<tag>C#高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-LINQ]]></title>
<url>%2F2019%2F07%2F28%2FC-LINQ%2F</url>
<content type="text"><![CDATA[LINQ定义一个匿名类型对象: var obj = new {myTitle = "anonymous type", myOtherParam = new int[] { 1, 2, 3, 4 } }; 扩展函数: 扩展方法必须在一个非嵌套、非泛型的静态类中定义 扩展方法必须是一个静态方法 扩展方法至少要有一个参数 第一个参数必须附加this关键字作为前缀 第一个参数不能有其他修饰符(比如ref或者out) 第一个参数不能是指针类型 LINQ 查询操作符.net的设计者在类库中定义了一系列的扩展方法来方便用户操作集合对象,这些扩展方法构成了LINQ的查询操作符扩展方法有Where, Max, Select, Sum, Any, Average, All, Concat等都是针对IEnumberable的对象进行扩展基本LINQ查询操作 获取数据源在LINQ查询中,第一步是指定数据源,在LINQ查询中,先使用from子句引入数据源和范围变量 筛选使用where子句生成结果,查询仅返回表达式为true的元素 中间件排序orederby子句根据要排序类型的默认比较器,对返回序列中的元素排序,字符串比较顺序默认从A到Z进行排序,逆序使用orderby…descendingz子句 分组group 子句用于对所获得的结果进行分组,使用group子句结束查询时,结果将以列表的形式列出,列表中的每个元素都是具有key成员的对象,列表中的元素根据该键杯分组,在循环访问生成组序列的查询时,必须使用嵌套foreach循环,外层循环访问每个组,内层循环访问每个组的成员,如果必须引用某个组操作的结果,可使用into关键字创建能被进一步查询的标识符 联接联接操作在不同序列间创建关联 选择(投影)Select子句指定在执行查询时产生的值的类型,查询表达式必须以select子句或group子句结尾 12345678910111213141516171819202122232425List<Customers> customers = new List<Customers>() { new Customers{ Name = "QIyanan", City = "Gansu", Age = 16 }, new Customers{ Name="IkeQi",City="USA",Age=18}, new Customers{ Name="kingQi",City="London",Age=20}, new Customers{ Name="kingZhang",City="London",Age=22}, new Customers{ Name="kingWang",City="London",Age=24}, new Customers{ Name="kingSun",City="London",Age=25}};var result = from customer in customers where customer.City == "London" && customer.Name == "kingQi" orderby customer.Age descending select customer;var queryCustomersByCity = from cust in customers group cust by cust.City;foreach(var customerGroup in queryCustomersByCity){ Console.WriteLine(customerGroup.Key); foreach(var customer in customerGroup) { Console.WriteLine($" {customer.Name}"); }}foreach (var customer in result){ Console.WriteLine(customer.Name);} 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859static void Main(string[] args){ var studentQuery = from student in students where student.Scores[0] > 90 && student.Scores[3] < 80 orderby student.Scores[0] descending select student; foreach (var student in studentQuery) { Console.WriteLine($" {student.Last}, {student.First}, {student.Scores[0]}"); } Console.WriteLine("==============================================="); var studentQuery2 = from student in students group student by student.Last[0]; foreach (var studentGroup in studentQuery2) { Console.WriteLine(studentGroup.Key); foreach (var student in studentGroup) { Console.WriteLine($" {student.Last}, {student.First}"); } } Console.WriteLine("==============================================="); var studentQuery3 = from student in students group student by student.Last[0] into studentGroup orderby studentGroup.Key select studentGroup; foreach (var groupOfStudents in studentQuery3) { Console.WriteLine(groupOfStudents.Key); foreach (var student in groupOfStudents) { Console.WriteLine(" {0}, {1}", student.Last, student.First); } } Console.WriteLine("==============================================="); var studentQuery5 = from student in students let totalScore = student.Scores[0] + student.Scores[1] + student.Scores[2] + student.Scores[3] where totalScore / 4 < student.Scores[0] select student.Last + " " + student.First; foreach (string s in studentQuery5) { Console.WriteLine(s); } Console.WriteLine("==============================================="); var studentQuery6 = from student in students let totalScore = student.Scores[0] + student.Scores[1] + student.Scores[2] + student.Scores[3] select totalScore; double averageScore = studentQuery6.Average(); Console.WriteLine($"Classaverage score = {averageScore}"); Console.WriteLine("==============================================="); var studentQuery7 = from student in students where student.Last == "Garcia" select student.First; Console.WriteLine("The garcias in the class are:"); foreach (string s in studentQuery7) { Console.WriteLine(s); }} LINQ查询函数Where(); 查询结果过滤1234List<int> list0 = new List<int> { 6, 4, 2, 7, 9, 0 };list0.Where(x => x > 5);list0.Where(x => x >= 1).Where(x => x <= 5);list0.Where(x => x >= 1 && x <= 5); Select(), SelectMany();处理结果会传回一个对象,这个对象可以是现在对象,也可以是匿名类型在LINQ语句中的select new语句,会自动被编译器转换Select()1234567var Num = from a in list0 where a > 3 select new { Number = a };Console.WriteLine(Num.FirstOrDefault().Number); 1var selectNum = list0.Where(x => x > 3).Select(x => new { Number = x }); SelectMany()类似于数据库中的CrossJoin1234567List<int> list1 = new List<int>() { 1, 2, 3, 4, 5 };List<int> list2 = new List<int>() { 6, 4, 5, 7, 9, 0 };var query = list1.SelectMany(x => list2);foreach (var iterm in query){ Console.WriteLine(iterm);} GroupBy()会按照给定的key(keySelector)以及内容elementSelector,产生群组后的结果GroupBy()设置了使用数列本身作为Key值,并且利用这个Key分组产生分组的数据12345678910List<int> list = new List<int> { 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 5, 5, 5, 6, 2, 3, 6 };var query = list.GroupBy(x => x);foreach (var item in query){ Console.WriteLine($"numer is {item.Key}, numer has {item.Count()}"); foreach (var x in item) { Console.WriteLine($" {x}"); }} ToLookUp()返回值是一个泛型Lookup<TKey, TElement>, 看起来和GroupBy()类似,但是Yolookup是立即执行,GroupBy是延迟执行12345678910111213141516var marrks = new[]{ new { Mark = 90, Group = "A"}, new { Mark = 80, Group = "B"}, new { Mark = 70, Group = "C"}, new { Mark = 60, Group = "D"},};var lookUpValue = marrks.ToLookup(x => x.Group);foreach(var item in lookUpValue){ Console.WriteLine("=========Group:{0}===========",item.Key); foreach(var result in item) { Console.WriteLine(result.Group+" "+result.Mark); }} Join将两个集合进行连接,即数据库中的Inner Join使用LINQ语句:12345678910111213141516171819202122232425var Mark = new[]{ new {Name = "C" , Mark = 65}, new {Name = "A" , Mark = 70}, new {Name = "B" , Mark = 80}};var Age = new[]{ new {Name = "B" , Age = 18}, new {Name = "C" , Age = 17}, new {Name = "A" , Age = 20}};var query = from mk in Mark join ee in Age on mk.Name equals ee.Name select new { name = mk.Name, mark = mk.Mark, age = ee.Age };foreach(var item in query){ Console.WriteLine($"name {item.name}, Score {item.mark}, Age {item.age}");} 使用LINQ函数:12345var quer1 = Mark.Join(Age, mk => mk.Name, ae => ae.Name, (mk, ae) => new { name = mk.Name, mark = mk.Mark, age = ae.Age });foreach (var item in quer1){ Console.WriteLine($"name {item.name}, score{ item.mark}, age{item.age}");} 其中Mark是第一个集合,Join方法的第一个参数Age是第二个集合,mk => mk.name 是 第一个集合的外键,ae=>ae.Name是第二个集合的外键。GroupJoin;将两个集合进行联接,并对结果进行分组12345678910111213//定义国家类class Country{ public string CountryName { get; set; }}//定义省类class Province{ public Country OwnerCountry { get; set; } public string ProvinceName { get; set; } public string ProvinceSize { get; set; } public string ProvinceSpot { get; set; }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869//对国家类进行实例化Country c1 = new Country(){ CountryName = "China"};Country c2 = new Country(){ CountryName = "America"};Country c3 = new Country(){ CountryName = "Korea"};Country c4 = new Country(){ CountryName = "Russian"};//对省实例化Province p1 = new Province(){ ProvinceName = "Hunan", ProvinceSize = "200", ProvinceSpot = "Old City", OwnerCountry = c1};Province p2 = new Province(){ ProvinceName = "Chicago", ProvinceSize = "150", ProvinceSpot = "Linkin Park", OwnerCountry = c2};Province p3 = new Province(){ ProvinceName = "Seoul", ProvinceSize = "100", ProvinceSpot = "Tian Tian World", OwnerCountry = c3,};Province p4 = new Province(){ ProvinceName = "Hunan", ProvinceSize = "200", ProvinceSpot = "Tian Meng Mountain", OwnerCountry = c1};Province p5 = new Province(){ ProvinceName = "Moscow", ProvinceSize = "80", ProvinceSpot = "Moscow University", OwnerCountry = c4};Province p6 = new Province(){ ProvinceName = "Moscow", ProvinceSize = "80", ProvinceSpot = "HelloWorld", OwnerCountry = c4}; 12345678910//定义省集合List<Province> provinces = new List<Province>{ p1,p2,p3,p4,p5,p6};//定义国家集合var countries = new List<Country>{ c1,c2,c3,c4}; 使用Join连接12345678910111213141516 //使用Join连接 var joinResult = countries.Join(provinces, country => country, province => province.OwnerCountry, (country, province) => new { countryName = country.CountryName, provinceName = province.ProvinceName, size = province.ProvinceSize, spot = province.ProvinceSpot }); foreach(var item in joinResult) { Console.WriteLine($"countryName is {item.countryName} , provinceName is {item.provinceName} , size is {item.size}, spot is {item.spot}"); } Console.ReadKey();} 使用GroupJoin12345678910111213var groupJoinResult = countries.GroupJoin(provinces, country=>country, province => province.OwnerCountry, (country,province) => new{ Name=country.CountryName, ProvinceSet=province.Select(x=>x)});foreach (var item in groupJoinResult){ Console.WriteLine(item.Name); foreach(var p in item.ProvinceSet) { Console.WriteLine(p.ProvinceName + " " + p.ProvinceSize + " " + p.ProvinceSpot); }}]]></content>
<categories>
<category>C#高级</category>
</categories>
<tags>
<tag>C#高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-多线程]]></title>
<url>%2F2019%2F07%2F28%2FC-%E5%A4%9A%E7%BA%BF%E7%A8%8B%2F</url>
<content type="text"><![CDATA[多线程什么是进程?狭义定义:进程就是一段程序的执行过程广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,他是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元进程的特点动态性,并发性,独立性,异步性进程的基本操作获取进程Process[] processes = Process. GetProcesses();启动进程: 1234ProcessStartInfo psi = newProcessStartInfo();//设置psi属性//命令行参数Process newProcess = Process.Start(psi); 杀掉进程:Process. Close()//并非真正结束进程,释放进程所占用的资源Process. Kill()//不建议使用什么是线程?线程,称为轻量级进程,是程序执行流的最小单元,一个标准的线程是由线程ID, 当前指令指针(PC), 寄存器集合和堆栈组成,线程是进程的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源多线程编程. NET程序中基本的线程 Garbage collector 线程:用于垃圾回收 Finalizer线程:用于调度对象的Finalize方法 Main线程:程序的额入口线程 UI线程:用于更新渲染用户界面,存在于Windows Form, WPF, Windows Store类型的程序中 Thread类123456789101112Thread t = new Thread(Run); //创建t.Start(); //启动t.Name = "Hello World"; //设置线程名t.isBackground = true; //设置为后台进程, 随着进程的退出线程也退出Thread.Sleep(1000); //挂起当前线程t.Join(); //阻塞当前线程,直到t线程结束t.Abort();终止t, 无条件退出, 报一个异常t.Interrupt(); //当t处于Wait,Sleep或Join时,终止t,且会报一个异常Thread类构造函数可使用下面的delegate作为参数:ThreadStart: public delegate void ThreadStart(); 线程池(ThreadPool)创建线程需要时间和资源,如果有很多不同的小任务要起线程来完成,可以使用ThreaPool创建和管理这些线程,ThreadPool管理着一个线程列表,可以在需要时增减线程的数量,这个数量的上限和下限都是可匹配的,也可以重复利用线程。和使用Thread类创建线程的方式不同,使用ThreadPool时,线程的创建,销毁以及并发线程数等因素皆有ThreadPool统一控制 12ThreadPool.QueueUserWorkItem(WaitCallback callBack);ThreadPool.QueueUserWorkItem(WaitCallback callback, Object state); 使用ThreadPool时应注意 ThreadPool中的线程均为后台线程,不能改成前台线程 ThreadPool中的线程不能被显式退出或暂停 ThreadPool中的线程不能被显式的修改线程明和控制优先级 ThreadPool中的线程不能被显式Join,必须使用其他的同步资源手段达到这个目的 正常的线程运行结束后会被销毁,而ThreadPool中的线程会被重复利用 ThreadPool中线程不应该用于管理常驻线程或其他需要一直运行的线程,这种情况应该是使用Thread类穿件一个新的线程 Task类Task是对ThreadPool进行一层抽象封装,旨在是开发专注于上层操作,不用关心底层Thread的控制 Task具有很强的灵活性 可以指定连续的工作 可以区分执行任务的成功与否 可以创建依赖关系,将任务层次化 两种表现形式:Task, Task, 后者表示可以返回结果 12var tf = new TaskFactory();var t1 = tf.StartNew(); var t2 = Task. Factory. StartNew(TaskMethod);var t3 = new Task(TaskMethod);t3. Start(); //表示异步启动Task连续执行Task t1 = new Task(DoOnFirst);Task t2 = t1. ContinueWith(DoOnSecond);Task t3 = t1. ContinueWith(DoOnSecond);Task t4 = t2. ContinueWith(DoOnSecond);Task t5 = t1. ContinueWith(DoOnError, TaskContinuationOptions, OnlyOnfaulted);TaskContinuationOptions枚举参数:OnlyOnFaulted, NotOnFaulted, OnlyOnCancled, NotOnCanceled, OnlyOnRanToToCompletion, ExcuteSynchronously…Task层次执行在父Task中创建一个子Task,就形成了层次依赖关系,父Task结束,则子Task也随着结束Task类常用方法ContinueWith: 创建一个在当前task执行结束后开始执行的新TaskDelay:创建一个在指定Delay时间后完成的TaskRun:返回一个压入ThreadPool的新TaskStart:开始一个TaskWait:等待当前实例Task结束WaitAll:等待所有Task结束WaitAny:等待任意Task结束WhenAll: 创建一个新的Task, 当被传作参数的所有Task都结束后,这个Task会被标识为结束WhenAny: 创建一个新Task, 当被传作参数的任意Task结束后,这个Task会被标识为结束CurrentId:获取当前正在执行的task IDTask类常用属性isCompleted: 判断当前task实例是否已经结束isFaulted:判断当前task实例是否由于一个未捕获的异常而结束Status:获取当前task实例的状态IsCanceled: 判断当前task实例是否被取消Exception:获取导致当前task结束的异常Factory:返回一个Task的工厂用来创建新taskID: 获取当前task实例ID 异步委托如果要创建一个轻量级的线程,一种简单的办法是使用委托(Delegate), 并异步调用它。委托通过线程池来完成异步任务可以使用不同的技术异步调用委托 Poll AsyncWaitHandle AsyncCallback 基本模型:IAsyncResult ar=delegate. BeginInvoke(…, AsyncCallback, …);object obj = delegaye. EndInvoke(ar);async和await关键字async关键字:标识一个方法是异步的。await关键字:用来等待一个异步方法的结果 线程同步进程内部的同步 Lock //区域性加锁,内部实现基于Monitor 123456789class Counter { int i; private object thisLock = new object(); public void Increase() { lock(thisLock) { i++; } }} Monitor 锁Monitor. Enter(thisLock)释放锁Monitor. Exit(this. Lock); //建议写在finally块中 InterLocked // AutoResetEvent类 //允许线程通过发信号互相通信 123AutoResetEvent are = new AutoResetEvent(false);are.WaitOne(); //阻塞当前线程,等待信号量are.Set(); //设置信号量,唤醒一个等待信号量的线程 多个进程中线程的同步 WaitHandle Mutex Semaphore Event ReaderWriterLockSlim 死锁所谓死锁是指两个或以上的进程在执行过程中,因正度资源而造成的一种互相等待的现象,若无外力作用,他们都无法推进下去。 创建多线程的步骤: 编写线程所要执行的方法 例化Thread类,并传入一个指向线程所要执行方法的委托。(这时线程已经产生,但还没有运行) 调用Thread实例的Start方法,标记该线程可以被CPU执行了,但具体执行时间由CPU决定 123456789101112131415161718192021222324252627282930313233namespace ConsoleApp2 { using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; class Program { static void Main() { var p1 = new ThreadTest(); var thread1 = new Thread(new ThreadStart(p1.Thread1)); //该代码可改写为var thread1 = new Thread(p1.Thread1); var thread2 = new Thread(delegate() { for (int i = 0; i < 10; i++) { Console.WriteLine("I am created by anonymous function"); } }); var thread3 = new Thread(() => { for (int i = 0; i < 10; i++) { Console.WriteLine("i am created by Lambda expression"); } }); thread1.Start(); thread2.Start(); thread3.Start(); Console.ReadKey(); } } class ThreadTest { public void Thread1() { Console.WriteLine("this is no parms function"); } }} C# 多线程与Java 多线程异同:都是通过Thread类来实现多线程都是调用start方法来开启一个线程 Java实现多线程一般有三种方法: 继承Thread类, 实现thread类里的run()方法 实现Runnable接口,重写run()方法,并将此对象以参数的形式传给Thread对象 实现Callable接口,重写Call()方法,并将此对象传给futureTask对象内,再将FutureTask对象作为参数传入Thread对象内。 C# 实现多线程: 通过ThreadStart委托来实现多线程 使用匿名方法来实现多线程 使用Lambda表达式实现多线程 ThreeadPool实现多线程,只要执行完毕,自动退出 ThreadPool. QueueUserWorkItem(p1. Thread1); //委托的方法必须有一个Object类型的参数 Task实现多线程 . NET程序集程序集,简单来说就是一个公共语言运行库(CLR)为宿主的,版本化的,自描述的二进制文件,程序集的构成: 程序集清单 元数据 实现这些类型的MSIL代码 资源集,诸如位图,指针,静态文本等。 程序集分类: 私有程序集(部署在本地) 共享程序集(部署在全局应用缓存——GAC) 类库类库是一个综合性的面向对象的可重用类型集合,这些类库包括:接口,抽象类和具体类类库不等于框架(Franework)类库会在编译之后生成一个dll文件,其他项目可以引用]]></content>
<categories>
<category>C#高级</category>
</categories>
<tags>
<tag>C#高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-委托]]></title>
<url>%2F2019%2F07%2F28%2FC-%E5%A7%94%E6%89%98%2F</url>
<content type="text"><![CDATA[委托:委托就是具有相同返回值类型和参数的方法的抽象 声明委托:delegate < retutrn type > 委托名(参数类型)委托是一个特殊的类,需要实例化,凡是可以声明类的地方,都可以声明委托 泛型委托:Predicate 泛型委托表示的方法需要传入一个T类型的参数,并且返回一个bool类型的返回值1234567891011121314var d1 = new Predicate<int>(Compare);static bool Compare(int a){ var flag = false; if (a > 5) { flag = true; return flag; } else { return flag; }} Action 泛型委托参数0-16个,类型不确定。不能有返回值1234var d1 = new Action<int,string>(Compare);static void Compare(int a,string b){} Func 泛型委托参数0-16个,类型不确定,必须有一个返回值,返回值为最后一个参数类型12345var d1 = new Func<int,string>(Compare);static string Compare(int a){ return "test Func";} 多播委托委托对象可使用“+”运算符进行合并,只有两个相同类型的委托可被合并“-”运算符可用于从合并的委托中移除组件委托1234567NumberChanger nc;var nc1 = new NumberChanger(AddNum);var nc2 = new NumberChanger(MultNum);nc = nc1;nc += nc2;nc(5);Console.WriteLine("Value of Num: {0}", getNum()); 输出结果:Value of Num: 75程序执行过程:初始化两个委托nc1,nc2,实现委托的合并操作,执行nc(5)时,实际先执行AddNum(),执行之后紧接着执行MultNum(),因此,结果为75.]]></content>
<categories>
<category>C#高级</category>
</categories>
<tags>
<tag>C#高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-集合]]></title>
<url>%2F2019%2F07%2F28%2FC-%E9%9B%86%E5%90%88%2F</url>
<content type="text"><![CDATA[集合(Collection)ArrayList: 动态数组,需要装箱与拆箱List: 与ArrayList类的泛型等效,性能提高 方法:添加元素:List. Add(T item); 添加一个元素List. AddRange(IEnumerable Collection); 添加一组元素Insert(int index, T item); 在index位置添加一个元素遍历List元素; 使用foreach进行遍历 删除元素:List. Remove(T item); 删除一个值List. RemoveAt(int index); 删除下标为index的元素List. RemoveRange(int index , int count); 从下标index开始,删除count个元素List. Contains(T item); 判断某个元素是否在该List中List. Sort(); 在List里面元素排序, 默认是元素第一个字母按升序List. Reverse(); 给List里面元素顺序反转List. Clear(); 清空ListList. Count(); 获取元素数目:List. Find(Predicatematch); 搜索与指定谓词所定义的条件相匹配的元素,并返回整个List中的第一个匹配元素 1234567891011121314151617181920var list = new List < string > ();string[] temArr = { "aq", "qwe", "qweqwr", "wqeqw", "ewq", "aeqw", "dad", "a"};list.AddRange(temArr);var str = list.Find(x => { if (x.Length == 1) { return true; } else { return false; }});Console.WriteLine(str); List. FindLast(); 搜索与指定谓词所定义的条件相匹配的元素,并返回整个List中的最后一个匹配元素List. TrueForAll(); 确定是否List中的每个元素都与指定的谓词所定义的条件相匹配List. FindAll(); 检查与指定谓词所定义的条件相匹配的所有元素List. Take(n); 获得前n行 返回值为IEnumetable, T的类型与List的类型一样List. RemoveAll(); 移除与指定谓词所定义的条件相匹配的所有元素,返回删除的个数Dictionary<[key], [value]>; 提供快速的基于键值的元素查找, 必须对键值类型进行说明创建及初始化:var myDictonary = new Dictionary< int, string>();添加一个元素:myDictonary. Add(1, "C#");通过Key查找元素:myDictonary[1];]]></content>
<categories>
<category>C#基础</category>
</categories>
<tags>
<tag>C#基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-运算符重载]]></title>
<url>%2F2019%2F07%2F28%2FC-%E8%BF%90%E7%AE%97%E7%AC%A6%E9%87%8D%E8%BD%BD%2F</url>
<content type="text"><![CDATA[运算符重载:重定义或重载 C# 中内置的运算符。因此,程序员也可以使用用户自定义类型的运算符。重载运算符是具有特殊名称的函数,是通过关键字 operator 后跟运算符的符号来定义的。与其他函数一样,重载运算符有返回类型和参数列表。12345678910111213141516171819202122232425262728293031323334class Box{ public double Length { get; set; } public double Width { get; set; } public double Heigth { get; set; } public double GetVolume() { return this.Length * this.Width * this.Heigth; } public static Box operator +(Box b, Box c) { var box = new Box(); box.Length = b.Length + c.Length; box.Width = b.Width + c.Width; box.Heigth = b.Heigth + c.Heigth; return box; }}static void Main(string[] args){ var box1 = new Box(); box1.Length = 10; box1.Width = 20; box1.Heigth = 30; var box2 = new Box(); box2.Length = 30; box2.Width = 40; box2.Heigth = 50; var box3 = box1 + box2; Console.WriteLine(box1.GetVolume()); Console.WriteLine(box2.GetVolume()); Console.WriteLine(box3.GetVolume()); Console.ReadKey();}]]></content>
<categories>
<category>C#基础</category>
</categories>
<tags>
<tag>C#基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-文件操作]]></title>
<url>%2F2019%2F07%2F28%2FC-%E6%96%87%E4%BB%B6%E6%93%8D%E4%BD%9C%2F</url>
<content type="text"><![CDATA[File I/ODirectory 类的常用属性public System.IO.DirectoryInfo Directory { get; }DirectoryName, Exists, isReadOnly, length(当前文件的的大小), Name Directory 类的常用方法:CreateDirectory()在指定路径中创建所有目录和子目录,除非他们已经存在, 如果该目录已存在,此方法不会创建一个新目录,但它将返回DirectoryInfo现有目录的对象。Delete()从指定路径删除空目录Delete(String, Boolean)删除指定的目录,并删除该目录所有的子目录和文件Move()移动目录public static void Move (string sourceDirName, string destDirName)GetDirectories()1public static string[] GetDirectories (string path, string searchPattern, System.IO.SearchOption searchOption); 1public static string[] GetDirectories (string path);//返回路径中子目录的完整名称的数组, 如果未找到任何目录,则为空数组 1public static string[] GetDirectories (string path, string searchPattern) string path: 相对路径或者绝对路径,此字符串不区分大小写string searchPattern: 与path中的子目录的名称匹配的搜索字符串,此参数可以包含有效文本个通配符的组合,不支持正则表达式 12345var directory = Directory.GetDirectories(@"c:\");foreach (var d in directory){ Console.WriteLine(d);} GetFiles()得到当前目录下所有文件public static string[] GetFiles (string path)Exists()指向现有目录,则为true,如果该目录不存在或者在尝试确定指定目录是否存在时出错,则为false1234567891011121314151617181920212223var directoryPath = @"c:\test";var filePath = @"MyTest.txt";if (!Directory.Exists(directoryPath))//尝试确定指定目录是否存在时,则为false{ Directory.CreateDirectory(directoryPath);}if (!File.Exists(Path.Combine(directoryPath,filePath))){ using (StreamWriter sw = File.CreateText(Path.Combine(directoryPath, filePath)))//文件夹路径必须存在, { sw.WriteLine("hello"); sw.WriteLine("and"); sw.WriteLine("welcome"); }}using (StreamReader sr = File.OpenText(Path.Combine(directoryPath, filePath))){ string s; while ((s = sr.ReadLine()) != null) { Console.WriteLine(s); }} DirectoryInfo类的常用属性:Exists; FullNam; Name; Parent; Root; DirectoryInfo类的常用方法:初始化:var dir = new DirectoryInfo(Path);Create()创建一个空目录Delete()public override void Delete ();//若当前目录为空则删除public void Delete (bool recursive);//指定是否删除子目录和文件CreateSubdirectory()在指定路径的基础上创建一个或多个子目录12var direcotaryInfo = new DirectoryInfo(@"c:\test\test\test");direcotaryInfo.CreateSubdirectory("text"); GetDirectories()public System.IO.DirectoryInfo[] GetDirectories ()GetFiles()public System.IO.FileInfo[] GetFiles ()public System.IO.FileInfo[] GetFiles (string searchPattern);1234foreach (var fi in di.GetFiles("*2*")){ Console.WriteLine(fi.Name);} MoveTo()public void MoveTo (string destDirName) File与FileInfo的区别:File是静态类,所有方法都是静态的,可以通过类名直接调用,不需要实例化,如果对文件进行少量操作,或者对很多文件进行操作,建议使用FileInfo是普通类,只有实例化对象之后才可以调用其中的方法,如果需要对一个文件进行大量操作,建议使用FileInfo File常用方法:Create(string path)Delete(string path)// 删除文件应先判空1234if (File.Exists(filePath)){ File.Delete(filePath);} Move()public static void Move (string sourceFileName, string destFileName)Copy()1public static void Copy (string sourceFileName, string destFileName)//复制以后不允许覆盖同名文件 ReadAllText(string path)public static string ReadAllText(string path)//打开一个文件,读取所有的行,然后关闭文件ReadAllLines(string path)public static string[] ReadAllLines(string path)ReadLines(string path)public static IEnumerable<string> ReadLines(string path)ReadAllBytes(string path)public static byte[] ReadAllBytes(string path) FileInfo类常用属性初始化:var fileinfo = new FileInfo(@"C:\test\test\test.txt");//路径必须是文件 FileInfo类的常用方法Create()public System.IO.FileStream Create ()Delete()public override void Delete ()MoveTo()public void MoveTo (string destFileName) //将指定文件移到新位置,提供要指定新文件名的选项CopyTo()public System.IO.FileInfo CopyTo (string destFileName)//不允许覆盖源文件1public System.IO.FileInfo CopyTo (string destFileName, bool overwrite)//可以覆盖源文件 遍历一个目录:12345678910111213public void GetAllFiles(string directoryPath){ var rootDirectory = new DirectoryInfo(directoryPath); foreach (var file in rootDirectory.GetFiles()) { Console.WriteLine($"File Name is {file.Name}"); } foreach (var directory in rootDirectory.GetDirectories()) { Console.WriteLine(directory.Name); this.GetAllFiles(directory.FullName); }} 文件内容操作12345Object——>MarshalByRefObject——>Stream——>FileStreamObject——>MarshalByRefObject——>TextReader——>StreramReaderObject——>MarshalByRefObject——>TextWriter——>StreamWriterObject——>MarshalByRefObject——>TextReader——>StringReaderObject——>MarshalByRefObject——>TextWriter——>StringWriter Strean流,提供了读写的方法是以字节码的形式从流中读取内容 FileStream处理的是字节,继承Stream,一个FileStream类的实例实际代表一个文件流,使用FileStream类可以对文件系统上的文件 进行读取,写入,打开和关闭操作public FileStream(string path, FileMode mode, FileAccess access);path指明文件搜所在路径信息mode是FileMode的枚举值,表示打开或者创建的方式CreateNew 创建新文件,如果文件已存在,则IOExcepptionCreate 创建新文件,文件已存在,源文件将被覆盖Open 打开文件,如果文件不存在则报FileNoFoundExceptionOpenOrCreate 打开文件,文件不存在则创建文件Truncate 打开已经存在的文件,清楚文件中的内容,保留文件的创建日期,如果文件不存在,则会跑出异常Append 打开先有文件并把Position设置在文档尾部,如果文件不存在将创建新文件,Append只能同FileAccess.Writer 一起使用access 是FileAccess的枚举值,它控制对文件的访问权限Read 打开文件用于只读Write 打开文件用于读写ReadWrite 打开文件用于读写创建文件:1var fileStream = new FileStream(@"C:\test\a.txt", FileMode.Create, FileAccess.Write); FileStream 类中常用属性bool CanRead 表示是否可以读取bool CanSeek 表示是否支持查找bool CanWrite 表示是否可以写入bool IsAsync 表示是异步还是同步打开bool Length 获取用字节表示的流长度string Name 获取文件的全路径int Read(byte[] array,int offset,int count) //从流中读取字节块并将数据写入给定缓冲区中1234567using (var fileStream = new FileStream(@"C:\test\a.txt", FileMode.Open, FileAccess.Read)){ var array = new byte[fileStream.Length]; fileStream.Read(array, 0, array.Length); var c = Encoding.UTF8.GetChars(array); Console.WriteLine(c);} void Write(byte[] array,int offset,int count) //将字节块写入文件流123456using (var fileStream = new FileStream(@"C:\test\a.txt", FileMode.Append, FileAccess.Write)){ var name = "qiyanan"; var arrayWrite = Encoding.UTF8.GetBytes(name); fileStream.Write(arrayWrite, 0, arrayWrite.Length);} StreamReader处理的是字符,从流中读取字符构造函数:StreamReader(Stream) //为指定的流初始化StreamReader类的新实例123456using (var fileStream = new FileStream(@"C:\test\a.txt", FileMode.Append, FileAccess.Write)){ using (var streanReader = new StreamReader(fileStream)) { }} StreamReader(String)为指定的文件名初始化StreamReader类的新实例123using (var streamReader=new StreamReader(@"c:\test\a.txt")){} StreamReader(Stream, Encoding)默认编码是Unicode, UTF-8是其子集123456using (var fileStream = new FileStream(@"C:\test\a.txt", FileMode.Append, FileAccess.Write)){ using (var streanReader = new StreamReader(fileStream, Encoding.GetEncoding("GB2312"))) { }} 常用方法Read()单字符读入public override int Read (); //返回值为ascll码ReadLine()行读入public override string ReadLine ()12345678using (var streanReader = new StreamReader(@"C:\test\a.txt")){ string Ichar = null; while ((Ichar=streanReader.ReadLine() )!= null) { Console.WriteLine(Ichar); }} StringReader从字符串读取字符 StreamWriterpublic override void Write(string value)//字符串写入12345using (var streamWriter=new StreamWriter(@"c:\test\a.txt",true)){ var str = "qi yanan is good "; streamWriter.Write(str);} public override void Write(char value) //字符写入public override void Write(char[] buffer)//字符数组写入public virtual void WriteLine () //写入空行,等同于跳到下一行` StringWriter字符串写入 Stream:BufferedStream //提供针对Stream的包装功能,使Stream支持缓存读写功能MemoryStream //提供对内存中一块数据的读写FileStream //提供基本的对文件进行二进制流读写的功能,与文件的类型无关UnmanagedMemoryStream //提供从托管代码访问非托管内存块的能力 文件操作注意事项:不要将文件流一次性读取到内存中,这样很容易造成内存泄漏一定要保证文件流被关闭,可以使用using和try…finallya两种方式用流读写数据时需要注意读写的长度是否准确,避免读写空字符判断StreamReader是否读写结束不可直接判断读取string是否为空StreamReader和StteramWriter是对文本的操作,应注明编码格式 对内存中一块数据的读写1234567891011121314public static void ShowMemoryString(string testString){ using (MemoryStream stream = new MemoryStream()) { if (stream.CanWrite) { byte[] buffer = Encoding.Default.GetBytes(testString); stream.Write(buffer, 0, buffer.Length); byte[] resultBuffer = stream.ToArray(); string resultString = Encoding.UTF8.GetString(resultBuffer); Console.WriteLine(resultString); } }} FileStream对文件进行字节流读写的功能,与文件的类型无关123456789public static void ShowFileContent(string filePath){ FileStream fileStream = File.Open(filePath, FileMode.Open); byte[] fileContentBuffer = new byte[fileStream.Length]; fileStream.Read(fileContentBuffer, 0, fileContentBuffer.Length); fileStream.Close(); string fileContent = Encoding.Default.GetString(fileContentBuffer); Console.WriteLine(fileContent);} 使用using语法糖省略文件流关闭操作1234567891011public static void ShowFileContent(string filePath){ byte[] fileContentBuffer; using(FileStream fileStream = File.Open(filePath, FileMode.OpenOrCreate)) { fileContentBuffer = new byte[fileStream.Length]; fileStream.Read(fileContentBuffer,0,fileContentBuffer.Length); } string fileContent = Encoding.Default.GetString(fileContentBuffer); Console.WriteLine(fileContent);} 分批次读入文件的内容1234567891011121314151617public static void CopyFileContent(string sourceFilePath, string destinationFilePath){ using (FileStream destinationFileStream = File.Open(destinationFilePath, FileMode.Create, FileAccess.Write)) { const int bufferSize = 4 * 1024 * 1024; byte[] fileContentBuffer = new byte[bufferSize]; using (FileStream sourceFileStream = File.Open(sourceFilePath, FileMode.Open, FileAccess.Read)) { int data; do { data = sourceFileStream.Read(fileContentBuffer, 0, fileContentBuffer.Length); destinationFileStream.Write(fileContentBuffer, 0, data); } while (data > 0); } }} FileMode 定义了四种打开文件的方法:Append: 打开一个已有文件,并将光标放置在文件的末尾,如果文件不存在,则创建文件Create: 创建一个新的文件,如果文件已存在,则删除旧文件,然后创建新文件CreateNew:创建一个新文件,如果文件已存在,则抛出异常Open:打开一个已有的文件,如果文件不存在,则抛出异常FileAccess成员有Read, ReadWrite 和Write]]></content>
<categories>
<category>C#高级</category>
</categories>
<tags>
<tag>C#高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-关键字]]></title>
<url>%2F2019%2F07%2F28%2FC-%E5%85%B3%E9%94%AE%E5%AD%97%2F</url>
<content type="text"><![CDATA[abstractabstract修饰符可用于类,方法,属性。在类声明中使用abstract指明这个类仅用作其他类的基类,而不用于实例化,标记为抽象的成员必须由派生自抽象类的非抽象类来实现12345678910111213141516171819202122232425262728293031abstract class Shape{ public abstract int x { get;} public abstract int GetArea();}class Square : Shape{ int side; public Square(int n) { this.side = n; public override int GetArea() { return side * side; } public override int x { get { return x + 10; } } interface I { void N(); } abstract class C : I { public abstract void N(); }} 抽象类功能: 抽象类不能实例化 抽象类可能包含抽象方法和访问器 无法使用sealed修饰符来修改抽象类 派生自抽象类的非抽象类,必须包含全部已继承的抽象方法和访问器的实际实现 抽象方法功能: 抽象方法是隐式的虚拟方法 只有抽象类中才允许抽象方法声明 抽象方法没有主体,仅以 ; 结尾,签名之后没有大括号 {} 抽象方法中不能使用static或virtual关键字修饰 抽象属性: 静态属性上不能使用abstract修饰符 通过包含使用override修饰符的属性声明,可在派生类中写抽象继承属性 async修饰符可将方法,lambda 表达式或匿名方法指定为异步。如果对方法或表达式使用此修饰符,则称其为异步方法 const使用const 关键字声明某个常量字段或常量局部变量,相当于Java中的static final 组合 readonlyconst关键字不同。const字段只能在该字段的声明中初始化。readonly字段可以在声明或者构造函数中初始化。因此,根据所需要使用的构造函数,readonly字段可能具有不同的值。 as将结果显示转换为给定的引用或可以为null值的类型,如果无法进行转换,则as运算符返回null,与强制类型转换不同,as运算符永远不会引发异常 ()强制转换运算符(T)E表达式E的结果显示的转换为T,如果转换失败,则发生编译时错误 TyprOf()用于获取某个类型的System.Type实例, base 关键字用于从子类中访问父类的成员, 调用基类上已被其他方法重写的方法, 指定创建子类实例跳跃父类构造函数 break语句将终止其所在位置的最接近封闭循环或switch语句,控制权传递给已终止语句后面的语句 checked对整型类型算术运算和转换显示启用溢出检查 default(T)生成T类型的默认值:T t = default(T); event声明发布服务器类中的事件,事件是天生的多播委托,没有赋值,只有+=以及-=操作,事件可以做到隔离保护,数据安全。123456789101112131415161718192021222324252627282930313233343536373839404142434445464748using System;namespace ConsoleApp3{ public delegate void GreetingDelegate(string name); public class GreetingManager { public event GreetingDelegate greetingDelegate; public void GreetPeople(string name) { greetingDelegate(name); } } class Program { private static void EnglishGreeting(string name) { Console.WriteLine("Morning , " + name); } private static void ChineseGreeting(string name) { Console.WriteLine("zao shang hao ," + name); } //public static void GreetPeople(string name, GreetingDelegate MakeGreeting) //{ // MakeGreeting(name); //} static void Main(string[] args) { GreetingManager gm = new GreetingManager(); gm.greetingDelegate += ChineseGreeting; gm.GreetPeople("ike qi"); gm.greetingDelegate += EnglishGreeting; //gm.greetingDelegate = EnglishGreeting; //gm.greetingDelegate += ChineseGreeting; gm.GreetPeople("qi ya nan "); //gm.GreetPeople("Ike Qi", EnglishGreeting); //GreetingDelegate greetingDelegate=new GreetingDelegate(EnglishGreeting); //greetingDelegate = EnglishGreeting; //greetingDelegate += ChineseGreeting; //greetingDelegate("qiyanan"); //GreetPeople("qiyanan",greetingDelegate); //greetingDelegate -= ChineseGreeting; //greetingDelegate("zhang hong chao "); Console.ReadKey(); } }} operator和implicit或explicit分别用于定义隐式转换或显式转换,定义转换的类型必须是该转换类型或目标类型。可用两种类型中的任何一种类型来定义两种用户定义类型之间的转换。 extern修饰符用于声明在外部实现的方法,还可以定义外部程序集别名,使得可以从单个程序集中引用同一组件的不同版本 finally通过使用 finally 块,可以清除 try 块中分配的任何资源,即使在 try 块中发生异常,也可以运行代码。 通常情况下,finally 块的语句会在控件离开 try 语句时运行。 fixed可防止垃圾回收器重新定位可移动的变量 is运算符检查表达式的结果是否与给定类型兼容 #lock #获取给定对象的互斥 lock,执行语句块,然后释放 lock。 持有 lock 时,持有 lock 的线程可以再次获取并释放 lock。 阻止任何其他线程获取 lock 并等待释放 lock。当同步对共享资源的线程访问时,请锁定专用对象实例private readonly object balanceLock=new object();避免将以下对象用作lock对象:this,Type实例,字符串实例,包括字符串文本 new 运算符创建类型的新实例, 可用作成员声明修饰符或泛型类型约束。 可以显式隐藏从基类继承的成员。 隐藏继承的成员时,该成员的派生版本将替换基类版本。 虽然可以不使用 new 修饰符来隐藏成员,但将收到编译器警告。 如果使用 new 来显式隐藏成员,将禁止此警告 out作为参数修饰符,它允许按引用而不是按值向方法传递参数。接口和委托的泛型类型参数声明中,该声明指定类型参数为协变。 params关键字可以指定采用数目可变的参数的方法参数。 params关键字之后不允许有任何其他参数,并且在方法声明中只允许有一个 params 关键字。params 参数类型必须是一维数组 ref关键字指示按引用传递的值要使用 ref 参数,方法定义和调用方法均必须显式使用 ref 关键 sealed修饰符可阻止其他类继承自该类 sizeof用于获取非托管类型的大小 可空类型(Nullable)C# 提供了一个特殊的数据类型,Nullable类型,表示一个数据类型可以赋值为空 ?在数据类型后面加上 ? 可使其变成可空类型int? x=null; ??如果 ?? 前面的操作数的结果是null,则返回 ?? 后面操作数的值,否则就是前面操作数的值]]></content>
<categories>
<category>C#基础</category>
</categories>
<tags>
<tag>C#基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-String成员]]></title>
<url>%2F2019%2F07%2F28%2FC-String%E6%88%90%E5%91%98%2F</url>
<content type="text"><![CDATA[String成员属性Chars(索引器): 获取string对象特定位置的字符 //java必须使用 charAt() 方法获取特定字符123456789static void Main(string[] args){ var str = "1234"; for(int i = 0; i < str.Length; i++) { Console.WriteLine(str[i]); } Console.ReadKey();} length: 获取字符串长度 //C#属于属性,Java属于方法length() String成员方法Compare(string, string):比较两个指定的 String 对象,并返回一个指示二者在排序顺序中的相对位置的整数,属于静态方法Format(): 格式化链接字符串public static string Format (string format, object arg0);Contains(): 返回一个bool,指示特定的字符是否在此字符串内public bool Contains (string value);StartsWith(): 确定此字符串实例的开头是否与指定的字符串匹配public bool StartsWith (string value);EndsWith(): 确定此字符串实例的结尾是否与特定的字符串匹配public bool EndsWith (string value);IndexOf(string): 字符串第一次出现的位置public int IndexOf (string value);LastIndexOf(String): 字符串最后一次出现的位置public int LastIndexOf (string value);Replace(string , string): 返回一个新的字符串,字符串调研该方法会把与字符串中第一个参数替换成第二个参数的内容public string Replace (string oldValue, string newValue);Split(Char[]): 分割字符串, 返回一个字符数组123456789static void Main(string[] args){ var str = "1;2;34"; var a = str.Split(';'); foreach (var b in a) { Console.WriteLine(b); }} Substring(int): 从特定位置切割字符串public string Substring (int startIndex);public string Substring (int startIndex, int length);从指定的字符位置开始且具有指定的长度ToLower(): 转换为小写模式ToUpper(): 转换为大写模式Trim(): 移除字符串中的所有前导和尾部空白字符OrdinalIgnoreCase(): 忽略字符串大小写进行比较 StringBuilder 常用属性Capacity: 获取或设置可包含在当前实例所分配的内存中的最大字符数Chars[Int32]: 获取或设置此实例中指定字符位置处的字符Length: 获取或设置当前 StringBuilder 对象的长度。MaxCapacity: 获取此实例的最大容量。 StringBuilder 常用方法Append(): 追加AppendFormat(“{0},{1}”,): 格式化追加Clear(): 从当前StringBuilder实例中移除所有字符Equals(Object): 确定指定的对象是否等于指定的对象Insert(int , String): 将字符串插入到指定字符位置Remove(int , int): 将指定范围的字符串从此实例中移除Replace(String , String): 将此实例中出现的所有指定字符串的替换为其他指定字符串 Object类的Equals方法:Equals(object a, object b): 比较值是否相等ReferenceEquals(object a, object b): 比较引用是否相等,对于值类型会先进行装箱,值类型会返回false==: 对于值类型和string,比较值是否相等,对于引用类型,比较引用是否相等]]></content>
<categories>
<category>C#基础</category>
</categories>
<tags>
<tag>C#基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C#-编码规范]]></title>
<url>%2F2019%2F07%2F28%2FC-%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83%2F</url>
<content type="text"><![CDATA[格式化代码:Crtl K + D ( Visual Studio )花括号 { } 尽量不要省略缩进使用四个空格, 不使用制表符 tab 命名规范:PasCal: 所有单词首字母大写Camel: 首单词首字母小写,其他单词首字母大写以下是具体的命名规范 通用命名约定——单词的选择 为标识符选择易于阅读的名字 可读性 避免使用下划线,连字符 不适用匈牙利命名方式 避免使用与关键字有冲突的标识符 尽量使用CLR通用类型名,例:String, int32, int64 … Namespace的命名:.(|) [[[. Subnamespace]], 如:AvePoint. PlatformRecovery. Backup不能根据公司的组织架构来定义命名空间 文件组织:不要在一个源文件中包含一个以上的公用类型,要用相同的名字命名文件与类文件结构和命名空间的结构是一致的 将using指令放在命名空间之内注释:代码足够清晰不用注释避免块注释不要把注释放在行尾,一般放在上面接口第一个字母大写 I基类使用 base 结尾Bool 类型使用 is,can 等前缀 异常处理:尽量使用系统异常使用 throw 抛出异常,不使用 throw e不使用异常写程序逻辑不要百分百吞掉异常 日志规范:要在异常日志中保留完整的堆栈信息不在循环体内打 log]]></content>
<categories>
<category>C#基础</category>
</categories>
<tags>
<tag>C#基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-File类]]></title>
<url>%2F2019%2F05%2F21%2FJava-File%E7%B1%BB%2F</url>
<content type="text"><![CDATA[在Java语言里提供有对于文件操作系统操作的支持,这个支持就在java.io.File类中进行了定义,File类是唯一一个与文件本身操作(创建,删除,重命名等等)有关的类。 File类的基本使用: File类是Comparable接口的一个子类,所以File类的对象是可以做排序处理的。 构造方法:public File(String pathname),设置要操作的完整路径;构造方法:public File(File parent, String child), 设置父路径与子目录 如果要进行文件的基本操作,可以使用如下的方法:创建新的文件:public boolean creatNewFile() throws IOException判断文件是否存在: public boolean exists();删除文件:public boolean delete(); 文件处理 在使用File类进行文件处理的时候需要注意的是:程序——> JVM ——> 操作系统函数——> 文件处理。 在进行文件创建的时候有一个重要的前提:文件的父路径必须首先存在。获取父路径:public File getParentFile();创建目录:public boolean mkdirs();1234567891011121314public class Main { public static void main(String[] args) throws IOException { File file = new File("D:/hello/demo/demo.txt"); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } if (file.exists()) { file.delete(); } else { System.out.println(file.createNewFile()); } }}]]></content>
<categories>
<category>Java高级</category>
</categories>
<tags>
<tag>Java高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-单例模式]]></title>
<url>%2F2019%2F05%2F12%2FJava-%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%2F</url>
<content type="text"><![CDATA[单例模式的核心本质在于:类内部的构造方法私有化,在类的内部产生实例化对象之后通过static方法获取实例化对象进行类中的结构调用,单例设计模式一般分为两类:懒汉式,饿汉式。 饿汉式12345678910111213141516171819202122232425262728package com.company;public class SingleTon { private SingleTon(){ System.out.println(Thread.currentThread().getName()); }; private static SingleTon instance = new SingleTon(); public static SingleTon getInstance(){ return instance; } public void print(){ System.out.println("线程执行"); }}package com.company;public class Main { public static void main(String[] args){ // write your code here for(int i=0;i<3;i++){ new Thread(()->{ SingleTon.getInstance().print(); },"线程"+i).start(); } }} 执行结果:1234线程0线程执行线程执行线程执行 饿汉模式的特点:类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,饿汉式天生是线程安全的,可以直接用于多线程而不会出现问题 懒汉式简单的懒汉模式1234567891011121314151617181920package com.company;public class SingleTon { private SingleTon() { System.out.println(Thread.currentThread().getName()); } private static SingleTon instance = null; public static SingleTon getInstance() { if (instance == null) { instance = new SingleTon(); } return instance; } public void print() { System.out.println("线程执行"); }} 执行结果123456线程1线程执行线程2线程执行线程0线程执行 该模式下程序是非线程安全的,为了实现线程安全可采用以下三种方式: 使用synchronized方法进行同步:1234567891011121314151617181920package com.company;public class SingleTon { private SingleTon() { System.out.println(Thread.currentThread().getName()); } private static SingleTon instance = null; public static synchronized SingleTon getInstance() { if (instance == null) { instance = new SingleTon(); } return instance; } public void print() { System.out.println("线程执行"); }} 执行结果:1234线程0线程执行线程执行线程执行 该方式虽然实现了延迟加载,但是和饿汉模式相比,它引入了同步关键字,因此,它的时耗要远远大于饿汉模式下实现单例。 使用双检锁的方法进行同步处理123456789101112131415161718192021222324package com.company;public class SingleTon { private SingleTon() { System.out.println(Thread.currentThread().getName()); } private static volatile SingleTon instance = null; public static SingleTon getInstance() { if (instance == null) { synchronized (SingleTon.class) { if (instance == null) { instance = new SingleTon(); } } } return instance; } public void print() { System.out.println("线程执行"); }} 运行结果:1234线程0线程执行线程执行线程执行 该方法虽然实现了线程同步,相比于同步方法的时耗会低一些,但任然不是最佳选择 使用内部类维护单例的实例12345678910111213141516171819package com.company;public class SingleTon { private SingleTon() { System.out.println(Thread.currentThread().getName()); } private static class SingletonHolder { private static SingleTon instance = new SingleTon(); } public static SingleTon getInstance() { return SingletonHolder.instance; } public void print() { System.out.println("线程执行"); }} 执行结果:1234线程0线程执行线程执行线程执行 在这个实现中,当SingleTon类被加载时,其内部类并不会被初始化,而当getInstance()方法被调用时,才会加载SingletonHoder, 从而初始化instance,由于实例的建立是在类加载时完成,故天生对多线程友好,getInstance()方法也不需要使用同步关键字,因此,这种实现方式时比较完美的。]]></content>
<categories>
<category>Java高级</category>
</categories>
<tags>
<tag>Java高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-Class类对象的三种实例化模式]]></title>
<url>%2F2019%2F05%2F09%2FJava-Class%E7%B1%BB%E5%AF%B9%E8%B1%A1%E7%9A%84%E4%B8%89%E7%A7%8D%E5%AE%9E%E4%BE%8B%E5%8C%96%E6%A8%A1%E5%BC%8F%2F</url>
<content type="text"><![CDATA[反射之中的所有核心操作都是通过Class类对象展开的,可以说Class类是反射操作的根源所在,如果要获得它的实例化对象,可以采用三种方式完成 【Object类支持】Object类可以根据实例化对象获取Class对象12345678public class Main { public static void main(String[] args) { Person person=new Person(); Class sr=person.getClass(); System.out.println(sr); }} JVM直接支持 类.class的形式实例化1234567public class Main { public static void main(String[] args) { Class sr=Person.class; System.out.println(sr); }} 通过Class.forName(“类名”)实例化123456789101112public class Main { public static void main(String[] args) { Class<?> sr= null; try { sr = Class.forName("Person"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(sr); }}]]></content>
<categories>
<category>Java高级</category>
</categories>
<tags>
<tag>Java高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-工厂模式+反射+泛型]]></title>
<url>%2F2019%2F05%2F09%2FJava-%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F-%E5%8F%8D%E5%B0%84-%E6%B3%9B%E5%9E%8B%2F</url>
<content type="text"><![CDATA[如果要想进行对象的实例化处理,除了可以使用关键字new之外,还可以使用反射机制来完成,那么到底是使用关键字new还是使用反射呢?最好的解释方案是通过工厂模式来解决 工厂模式工厂模式最大特点:客户端的一个程序类不直接牵扯到对象的实例化管理,只与接口发生关联,通过工厂类获取指定接口的实例,传统的工厂模式:1234567891011121314151617181920212223242526272829303132333435363738394041424344454647package com.company;public interface Imessage { //消息接口 void send();}package com.company;public class NetMessage implements Imessage { @Override //网络消息类 public void send() { System.out.println("【网络消息发送】"); }}package com.company;public class CloudMessage implements Imessage { @Override //云消息类 public void send() { System.out.println("【云消息发送】"); }}package com.company;public class Factory {//工厂类 private Factory(){}; public static Imessage getInstance(String className){ if("netmessage".equalsIgnoreCase(className)){ return new NetMessage(); }else if("cloudMessage".equalsIgnoreCase(className)){ return new CloudMessage(); } return null; }}package com.company;public class Main { public static void main(String[] args) { // write your code here Imessage netMessage=Factory.getInstance("netmessage"); netMessage.send(); Imessage cloudmessage=Factory.getInstance("cloudmessage"); cloudmessage.send(); }} 此种工厂设计模式属于静态工厂设计模式,如果现在需要追加一个子类,那么工厂模式必须修改,如果不追加此种判断是无法获取指定接口对象的,因此,我们通过工厂模式 + 反射机制来解决问题。 工厂模式 + 反射123456789101112131415161718192021222324252627282930313233343536package com.company;import java.lang.reflect.InvocationTargetException;public class Factory { private Factory() { } public static Imessage getInstance(String className) { Imessage instance = null; try { instance = (Imessage) Class.forName(className).getDeclaredConstructor().newInstance(); } catch (Exception e) { e.printStackTrace(); } return instance;// if("netmessage".equalsIgnoreCase(className)){// return new NetMessage();// }else if("cloudMessage".equalsIgnoreCase(className)){// return new CloudMessage();// }// return null; }}package com.company;public class Main { public static void main(String[] args){ // write your code here Imessage netMessage=Factory.getInstance("com.company.NetMessage"); netMessage.send(); Imessage cloudmessage=Factory.getInstance("com.company.CloudMessage"); cloudmessage.send(); }} 当我们在开发过程中,可能存在有大量的接口,并且这些接口都可能需要通过工厂类实例化,此时,该工厂既不能只为一个特定的接口服务,而是应该为所有的接口服务,因此,我们采用工厂模式 + 反射 + 泛型来解决 工厂模式 + 反射 + 泛型123456789101112131415161718192021222324252627282930package com.company;import java.lang.reflect.InvocationTargetException;public class Factory { private Factory() { } public static <T> T getInstance(String className, Class<T> tClass) { T instance = null; try { instance = (T) Class.forName(className).getDeclaredConstructor().newInstance(); } catch (Exception e) { e.printStackTrace(); } return instance; }}package com.company;public class Main { public static void main(String[] args){ // write your code here Imessage netMessage=Factory.getInstance("com.company.NetMessage",Imessage.class); netMessage.send(); IService houseService=Factory.getInstance("com.company.HouseService",IService.class); houseService.service(); }} 此时的工厂设计模式将不再受限指定的接口,可以为所有的接口提供实例化服务]]></content>
<categories>
<category>Java高级</category>
</categories>
<tags>
<tag>Java高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-多线程]]></title>
<url>%2F2019%2F05%2F02%2FJava-%E5%A4%9A%E7%BA%BF%E7%A8%8B%2F</url>
<content type="text"><![CDATA[在Java中,实现多线程一般有三种方式: 继承Thread类,实现run()方法,调用start()方法,启动一个线程代码如下:123456789101112131415161718192021222324public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(currentThread().getName()); } }}public class Main { public static void main(String[] args) { MyThread myThreadA = new MyThread(); MyThread myThreadB = new MyThread(); MyThread myThreadC = new MyThread(); myThreadA.start(); myThreadB.start(); myThreadC.start(); }} 实现Runnable接口,重写run()方法,并将此对象以参数的形式传给Thread对象,并调用Thread对象的start()方法,开启线程代码如下:12345678910111213141516171819202122232425public class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "我正在执行"); } }}public class Main { public static void main(String[] args) { MyThread myThread=new MyThread(); Thread threadA = new Thread(myThread, "线程A"); Thread threadB = new Thread(myThread, "线程B"); Thread threadC = new Thread(myThread, "线程C"); threadA.start(); threadB.start(); threadC.start(); }} 实现Collable接口,重写Call()方法, 并将它传让FutureTask对象内,再将FutureTask对象传入Thread对象内,调用Thread对象的start()方法开启线程123456789101112131415161718192021import java.util.concurrent.Callable;public class MyThread implements Callable<String> { @Override public String call() { for (int i = 0; i < 10; i++) { System.out.println("线程执行" + i); } return "线程执行完毕"; }}import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class Main { public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<String> futureTask = new FutureTask<String>(new MyThread()); new Thread(futureTask).start(); System.out.println(futureTask.get()); }} Runnable 接口和Callable 的区别: Runnable 是在JDK 1.0 的时候提出的多线程实现接口,而Callable是在JDK1.5 之后提出的; java.lang.Runnable 接口之中只提供有一个run()方法,并且没有返回值; java.util.concurrent Callable 接口提供有call() 方法,可以有返回值。想要实现多线程的启动,必须使用Thread类的start()方法]]></content>
<categories>
<category>Java高级</category>
</categories>
<tags>
<tag>Java高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-继承与组合]]></title>
<url>%2F2019%2F04%2F29%2FJava-%E7%BB%A7%E6%89%BF%E4%B8%8E%E7%BB%84%E5%90%88%2F</url>
<content type="text"><![CDATA[继承继承(inheritance)是指Child类的对象可以使用仅对Father类的对象有效的方法或者属性,它使得这些方法和属性就好像是Child类自己定义的一样,此时Father类是Child类的父类,Child类是Father类的子类。在类结构中,父类的内部对于子类是可见的,所以,通过继承的代码复用是一种“白盒式代码复用”。 组合组合(composition)是指通过对现有的对象进行拼装(组合)产生新的更复杂的功能。因为在对象之间,各自的内部细节是不可见的,所以,称这种代码复用是“黑盒式代码复用”。1234567class Queue extends Array{ // 继承}class Queue extends Object{ private Array asArray //组合}]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-OOP]]></title>
<url>%2F2019%2F04%2F29%2FJava-OOP%2F</url>
<content type="text"><![CDATA[什么是面向对象的程序设计?什么是面向过程的程序设计程序设计范型,开发程序发的方法,基于面向过程面向过程:分析出解决问题所需要的步骤,使用函数把这些步骤实现,通过顺序执行一组组语句来实现一个个功能,这些语句的执行过程就是整个程序例如:计算器程序,在main()函数里定义一系列的数据结构,然后开始写加减乘除等函数,实现功能面向对象:抽象度更高,眼光集中在解决问题本身,他的目标是使模块的抽象度更高,实现可复用。计算器创建一个Calculator类,在类里定义必要的数据成员,实现功能。 什么是对象?万物皆对象,不但包括具体存在的,还包括抽象的规则,计划等。对象具有状态,可以用数据来描述,例如:一个人,姓名:齐**,年龄:22,体重:65,身高:175等对象具有行为,例如,运动,吃饭,睡觉,敲代码!!! 什么是类?具有相同或相似性质的对象的集合就是类,例如:人,学生,动物。。。类具有属性:是对象的抽象,例如:姓名,年龄,体重。。。类具有行为:是对对象行为的抽象。。。抽象类:一般情况下,除了继承树的叶子节点以外,其他的所有类都是抽象类具体类:继承树的叶子节点一般情况下是具体类 类的实例化通过类产生对象,叫做类的实例化, 用关键字new语法: 类 实例名 = new 类(); 运用反射手段,调用java.long.Class 或者 java.lang.reflect.Constructor 类的newInstance()实例方法。 运用对象的clone()方法 运用反序列化手段,调用java.io.ObjectInputStream 对象的 readObjet()方法。1 和2 都会明确的,显式的调用构造函数;3 是在内存上对已有对象的克隆,所以不会调用构造函数;4 是从文件中还原类的对象,也不会调用构造函数 继承!!!面向对象中类与类之间的一种关系,继承的类称为子类,派生类,而被继承的类称为父类,基类,或者超类,类与类继承之后,除了构造方法,子类具有父类的所有属性和方法,构造方法是调用,不是继承,同时子类可以加入新的属性或者方法。 继承的设计原则:高内聚,低耦合,类的继承层数不能超过三层。如何让对象访问父类有参构造函数??super封装概念:类将内部数据隐藏,为用户提供对象的属性和行为的接口,用户通过这些接口使用这些类,无需知道这些类内部是如何构成的,不能操作类中的内部数据目的:防止对实现细节的访问封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。一个访问修饰符 定义了一个类成员的范围和可见性。支持的访问修饰符如下所示:Public 完全公开,没有访问限制,;Private 私有成员,在类的内部可以使用,子类,实例都不能访问;Protected 保护成员,该类的内部和继承类中可以访问 多态通过继承实现的不同对象调用相同的方法,表现出不同的行为,称之为多态 接口(Interface)接口就是一种特别的类。只需要定义函数,函数不用实现,接口内定义的函数必须在子类里面全部得实现可以实现多继承]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-重载与重写]]></title>
<url>%2F2019%2F04%2F29%2FJava-%E9%87%8D%E8%BD%BD%E4%B8%8E%E9%87%8D%E5%86%99%2F</url>
<content type="text"><![CDATA[重载重载(overload) 指编写一个与已有函数同名但是函数参数不同的函数,重载不是一种面向对象的编程,而只是一种语法规则,重载与多态没有直接关系。 特征 相同的范围(在同一个类中) 函数名字相同 参数不同 virtual关键字可有可无 重写重写(override) 指派生类重写基类的虚函数,重写的函数必须有一致的参数表和返回值 特征 不同的范围(分别位于派生类与基类) 函数名字相同 参数相同 基类函数必须有virtual关键字]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-成员内部类的访问方式]]></title>
<url>%2F2019%2F04%2F28%2FJava-%E6%88%90%E5%91%98%E5%86%85%E9%83%A8%E7%B1%BB%E7%9A%84%E8%AE%BF%E9%97%AE%E6%96%B9%E5%BC%8F%2F</url>
<content type="text"><![CDATA[方式一:在外部提供一个方法创建内部类的对象进行访问。方式二:在其它类直接创建内部类的对象,格式: 内部类 变量名 = new 外部类().内部类();内部类需要注意的细节: 如果在外部类与内部类存在同名的成员变量时,在内部类中默认情况下是访问内部类中的成员变量,可以通过外部类.this.成员变量 来访问外部类的成员变量。 123456789101112131415161718192021public class Test { public int a = 1; public class Tes { public int a = 3; public void out() { System.out.println("内部类的a值:" + a); System.out.println("外部类的a值:" + Test.this.a); } } public Test() { new Tes().out(); } public static void main(String[] args) { new Test(); }} 私有的成员内部类只能在外部类提供一个方法创建内部类的对象进行访问,不能再其他类创建对象。 成员内部类一旦出现了静态成员,那么该类也必须使用static修饰。局部内部类在一个类的方法内部定义了另外一个类,那么方法内的类称为局部内部类注意:如果局部内部类访问了局部变量,那么该变量需要用final修饰]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-Object类的方法]]></title>
<url>%2F2019%2F04%2F28%2FJava-Object%E7%B1%BB%E7%9A%84%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[Object 基类的方法如下:clone(): 创建并返回此对象的一个副本。equals(Object obj): 指示某个其他对象是否和此对象“相等”。finalize(): 当垃圾回收器确定不存在对该对象的的个更多引用时,由对象的垃圾回收器调用此方法。getClass(): 返回一个对象的运行时类。hashCode(): 返回对象的哈希值。notify(): 唤醒在此对象监视器上等待的单个线程。notifyAll():唤醒在此对象监视器上等待的所有线程。toString(): 返回该对象的字符串表示。wait(): 导致当前线程等待,直到其他线程调用此对象的notify()或者notifyAll()。wait(long timeout): 导致当前线程等待,直到其他线程调用此对象的notify()或者notifyAll(), 或者超过指定的时间量。wait(long timeout , int nanos): 导致当前线程等待,直到其他线程调用此对象的notify()或者notifyAll(), 或者其他某个线程中断当前线程,或者超过指定的时间量。]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-抽象类和接口]]></title>
<url>%2F2019%2F04%2F28%2FJava-%E6%8A%BD%E8%B1%A1%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3%2F</url>
<content type="text"><![CDATA[抽象类有时候,基类并不与具体的事物相联系,而是只表达一种抽象的概念,用以为它的派生类提供一个公共的界面,为此,Java引入了抽象类(abstract class)的概念.一个抽象类要注意以下几点: 抽象类只能做为其他类的基类,它不能直接被实例化,且不能使用new操作符; 抽象类允许包含抽象成员,但不是必须的,抽象类可以有非抽象方法; 抽象类不能同时是final的; 如果一个非抽象类从抽象类中诞生,则必须通过重写来实现继承而来的抽象成员; 抽象类可以被抽象类所继承,结果仍是抽象类; 抽象类允许被声明。123456789101112131415161718192021222324252627282930313233abstract class Person //建立抽象类{ public abstract void SayHello(); //抽象方法SayHello public void about() { System.out.println("Abstract Demo"); }}class Student extends Person //实体类Student继承Person{ public void SayHello() { // 重写抽象方法SayHello System.out.println("SayHello"); }}class Nurse extends Person //建立实体类Nurse继承Person{ //没有重写抽象方法SayHello,即这个类是错误的}abstract class Pupil extends Person //建立抽象类Pupil继承Person{ public void SayHello() { System.out.println("SayHello"); }}abstract class Worker extends Person //建立抽象类Work继承Person{ //抽象类继承抽象类,可以不用重写抽象方法} 接口 接口用于描述系统对外提供的所有服务,因此接口中的成员常量和方法都必须是公开的(public)类型的,确保外部使用者能够访问他们; 接口仅仅描述系统做什么,但不知名如何去做,所以接口中的方法都是抽象(abstract)方法; 接口不涉及和任何具体实例相关的细节,因此,接口没有构造方法,不能被实例化,没有实例变量,只有静态(static)变量; 接口中的变量是所有实现类共有的,即,变量是final类型,也就是常量了; 接口中不能出现变量,属性只能读,不能改; 接口默认的方法是abstract的,接口属性默认是public static final ,且必须赋初值。]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-集合]]></title>
<url>%2F2019%2F04%2F28%2FJava-%E9%9B%86%E5%90%88%2F</url>
<content type="text"><![CDATA[java 容器类库一共有两种主要类型:Collection和MapCollection 和Map 的区别在于每个容器内每个“槽”所存的元素个数不同,Collection 类型中,每个“槽”只有一个元素,Map类型中,持有key-value关联,类似小型数据库,所有的Java容器都可以自动调整自己的尺寸。各自旗下的子类关系如图:Collection:Map:Collection 的子类如下:  ——List: 将以特定顺序存储元素,所以取出来的顺序可能和放入顺序不同    ——ArrayList: 擅长随机访问元素,但在List中插入,删除,移动元素较慢    ——LinkedList: 插入,删除,移动元素方便,随机访问元素慢  ——Set:每个值只能保存一个对象,不能含有重复的元素    ——HashSet:使用散列函数    ——TreeSet:使用红黑树    ——LinkedHashSet 使用链表结合散列函数    ——Queue:队列,先进先出的容器Map 的子类如下:  ——HashSet  ——HashTable  ——TreeMap 其他特征: List, Set, Map将所有对象视为Object类型; Collection, List, Set, Map 都是接口,不能被实例化在各种List中,最好的做法是以ArrayList作为默认选择。当插入,删除频繁时,使用LinkedList(), Vector总是比ArrayList慢,在各种Set中,HashSet通常优于HashTree(插入,查找)。只有当需要产生一个经过排序的序列时,采用TreeSet。Hashtree的意义是用来维护其内元素的排序;在各种Map中,HashMap用于快速查找,当元素的个数固定时,Array的效率是最高的HashMap与HashTable的区别都属于Map接口的类,实现了将唯一键映射到特定的值上。HashMap 类没有分类或者排序,它允许一个null键和多个null值HashTable 不允许null键和null值。HashTable的方法是Synchronize的,HashMap不是]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-内存管理]]></title>
<url>%2F2019%2F04%2F28%2FJava-%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%2F</url>
<content type="text"><![CDATA[垃圾收集Java中使用被称为垃圾收集器的技术来监视Java程序的运行,当对象不在使用时,就自动释放对象所使用的内存。垃圾收集器是自动运行的,一般情况下,无须显式的请求垃圾收集器,程序运行时,垃圾收集器会不时检查对象的各个引用,并非是无引用对象所占用的内存。调用System 类中的静态gc()方法可以运行垃圾收集器,但这样并不能保证立即回收指定对象。Java垃圾回收机制:gc即垃圾收集机制,是指JVN用于释放那些不再使用的对象所占用的内存。Java的垃圾回收机制时为所有的Java应用进程服务的,而不是为某个特定的进程服务的,因此,任何一个进程都不能命令垃圾回收机制做什么,怎么做或做多少,在JVM垃圾收集器收集一个对象之前,一般要求程序中调用适当的方法来释放资源,但在没有明确释放资源的情况下,Java提供了默认机制终止化该对象来释放资源,这个方法就是finalize()。在finalize()方法返回之后,对象消失,垃圾收集开始执行。 一块内存空间是否符合垃圾收集器收集标准1.给对象赋予了控制null, 以后再也没有调用过2.给对象赋予了新值,即重新分配了内存空间一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就一定会被垃圾收集器收集 内存管理变量的内存分配情况1.不要试图去假定垃圾收集发生的时间,这一切都是未知的。2.Java提供了一种强行执行的垃圾收集的方法——调用System.gc(),但这是一个不确定方法。3.挑选适合自己的垃圾收集器4.防止内存泄漏5.尽早释放无用对象的引用,对于频繁申请内存和释放内存的操作,最好使用finalize强制执行,或者写自己的finalize方法。 什么是Java中的内存泄漏在Java中,内存泄漏就是存在一些被分配的对象,这些对象有两个特点:1.对象是可达的,即在有向图中,存在通路可以与其相连;2.对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象可以判定位Java中的内存泄漏,这些对象不会被gc所回收,然而他们却占用内存。例如:123456Vector v = new Vector(10);for (int i = 1; i < 100; i++){ Object o = new Object(); v.add(o); o = null;} 内存泄露的主要原因:保留下来却永远不再使用的对象引用。 cloneJava中clone的含义是,假设x是一个非空对象,则:123x.clone() != x //true , 就是说他们不是一个对象x.clone().getClass() == x.getClass() //true, 说明他们是同一个类型的classx.equals(x.clone()) //true, 说明值相等 clone方法是在Object中定义的,而且是proteced 的,只有实现了Cloneable接口,才可以在该类的实例上调用clone方法,否则会抛出CloneNotSupportException,该方法从Object类中继承而来,该接口只是一个标记。]]></content>
<categories>
<category>Java高级</category>
</categories>
<tags>
<tag>Java高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-序列化]]></title>
<url>%2F2019%2F04%2F28%2FJava-%E5%BA%8F%E5%88%97%E5%8C%96%2F</url>
<content type="text"><![CDATA[如何实现Java的序列化?序列化一个对象只需要让他实现Serializable接口(这是一个“标记接口”,tagging interface,没有任何方法)就行,但是,当语言引入序列化概念之后,它有很多标准类库的类,包括primitive的wrapper类,所有的容器类,以及别的很多类,都会相应的发生改变,甚至连Class对象都会被序列化。要想序列化对象,必须先创建一个OutputStream,然后把它嵌进ObjectOutputStream。这是就能调用writeObject() 方法把对象写入OutputStream, 读的时候需要把InputStream嵌到ObjectInputStream中,然后在调用readObject()方法。代码如下:1234567891011121314151617181920212223242526272829303132public class SerSingleton implements java.io.Serializable{ String name; private SerSingleton(){ System.out.println("Singleton is create"); name = "SerSingleton"; } private static SerSingleton instance = new SerSingleton(); public static SerSingleton getInstance(){ return instance; } public static void createString(){ System.out.println("CreateString in Singleton"); } private Object readResolve(){//阻止新生成的实例,总是返回当前对象 return instance; }}public void test() throw Exception{ SerSingleton s1 = null ; SerSingleton s = SerSingleton.getInstance(); //先将实例串化到文件 FileOutputStream fos = new FileOutputStream("SerSingleton.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s); oos.flush(); oos.close(); //从文件读出原来的单例类 FileInputStream fis = new FileInputStream("SerSingleton.txt"); ObjectInputStream ois = new ObjectInputStream(fis); s1 = (SerSingleton) ois.readObject(); Assert.assertEquals(s,s1); }]]></content>
<categories>
<category>Java高级</category>
</categories>
<tags>
<tag>Java高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-输入/输出流]]></title>
<url>%2F2019%2F04%2F28%2FJava-%E8%BE%93%E5%85%A5-%E8%BE%93%E5%87%BA%E6%B5%81%2F</url>
<content type="text"><![CDATA[  Java的IO操作有面向字节(Byte)和面向字符(Character)两种方式。  面向字节的操作以8位对二进制数据进行操作,对数据不进行转换,对数据不进行转换,这些类都是InputStream 和OutputStream 的子类,  面向字符的操作以字符为单位进行操作,在读的时候将二进制数据转换为字符,在写的时候将字符转换为二进制数据,这些类都是Reader和Writer的子类。  即以InputStream(输入)/OutputStream(输出)为后缀的是字节流,以Reader(输入)/Writer(输出)为后缀的是字符流。使用FileOutputStream的标准输出12345678910111213public class Main { public static void main(String[] args) throws IOException { File file = new File("D:/hello/demo/demo.txt"); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } OutputStream outputStream=new FileOutputStream(file,true); String str="hello world"; outputStream.write(str.getBytes()); outputStream.close(); }} 该程序在整体处理之中,只是创建了文件的父目录,但是并没有创建文件,在程序执行后发现文件可以自动帮助用户创建。使用FileInputStream的标准输入1234567891011public class Main { public static void main(String[] args) throws IOException { File file = new File("D:/hello/demo/demo.txt"); InputStream inputStream=new FileInputStream(file); byte data[]=new byte[1024]; int len=inputStream.read(data); System.out.println("["+new String(data,0,len)+"]"); inputStream.close(); }} 在Writer类里面提供有许多的输出操作方法:输出字符数组:public void write(char[] char) throws IOException输出字符串:public void write(String str) throws IOException使用FileWriter标准输出1234567891011121314public class Main { public static void main(String[] args) throws IOException { File file = new File("D:/hello/demo/demo.txt"); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } Writer writer = new FileWriter(file); String str = "hello,world"; writer.write(str); writer.append("中国人民万岁"); writer.close(); }} 使用Writer输出的最大优势在于可以直接利用字符串完成,Writer是字符流,字符处理的优势在于中文处理Reader类并没有像Writer一样提供有整个字符串的输入处理操作,只能够利用字符数组来实现接收:接收数据:public int read(char[] cbuf) throws IOException:使用FileReader标准输入12345678910111213public class Main { public static void main(String[] args) throws IOException { File file = new File("D:/hello/demo/demo.txt"); if (file.exists()) { Reader reader = new FileReader(file); char data[] = new char[1024]; int len = reader.read(data); System.out.println(new String(data, 0, len)); reader.close(); } }} 字符流读取的时候只能按照字符数组的形式来实现处理操作下面是Java流类图结构:]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-静态变量与私有变量]]></title>
<url>%2F2019%2F04%2F27%2FJava-%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F%E4%B8%8E%E7%A7%81%E6%9C%89%E5%8F%98%E9%87%8F%2F</url>
<content type="text"><![CDATA[static 关键字,可以被应用到类,方法和变量中 static变量  static变量称作静态变量,静态变量被所有对象共享,内存中只是一个副本,当且仅当被初次加载时会被初始化 static方法  static方法是没有this的方法,在static方法内部不能调用非静态方法,反过来是可以的,可以在没有创建任意对象的前提下通过本身调用static方法。 Java静态类  如果一个类被声明为static,只有一种情况,就是静态内部类(内嵌类)。如果在外部类声明static,程序编译不会通过。1)静态内部类和静态方法一样,只能访问静态的成员变量和方法,不能访问非静态的方法和属性,但是普通类可以访问任意外部类的成员变量和方法2)静态内部类可以声明普通成员变量和方法,而普通内部类不能声明static成员变量和方法3)静态内部类可以单独初始化Inner i = new Outer.Inner();普通内部类初始化:12Outer n = new Outer();Inner i = o.new Inner(); 静态内部类使用场景一般是当外部类需要使用内部类,而内部类无需外部类资源,并且内部类可以单独创建的时候会考虑采用静态内部类的设计。1234567891011121314151617181920212223242526272829303132public class Outer { private String name; private int age; public static class Builder { private String name; private int age; public Builder(int age) { this.age = age; } public Builder withName(String name) { this.name = name; return this; } public Builder withAge(int age) { this.age = age; return this; } public Outer build() { return new Outer(this); } } private Outer(Builder b) { this.age = b.age; this.name = b.name; }} 静态内部类调用外部类的构造函数,来构造外部类,由于静态内部类可以被单独初始化,所以有了以下实现1234public Outer getOuter(){ Outer outer = new Outer.Builder(2).withName("Ike").build(); return outer;}]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-传递与引用]]></title>
<url>%2F2019%2F04%2F27%2FJava-%E4%BC%A0%E9%80%92%E4%B8%8E%E5%BC%95%E7%94%A8%2F</url>
<content type="text"><![CDATA[Explain call by value and call by reference. Which of this these two does Java support?不管Java参数的类型是什么,一律传递参数的副本对于基本类型而言,传值就是把自己复制一份传递,即使自己的副本变了,自己也不变。对于对象类型而言,它传的引用副本指向自己的地址,而不是自己实际值的副本,原因以下: 对象类型是存储在堆里,速度相对于基本类型比较慢; 对象类型本身比较大,如果采用重新复制对象值的办法,浪费内存且速度又慢。以参数形式传递简单类型的变量时,实际上是将参数的值作为一个副本传进方法函数的,那么在方法函数中不管怎么改变其值,其结果都是改变了副本的值,而不是源值。123456789101112public class Test{ public static void test (Boolean test){ test = !test; System.out.println("In test(boolean) : test = " + test); } public static void main(String[] args){ boolean test = true ; System.out.println("Before test(boolean) : test = " + test); test(test); System.out.println("After test(boolean) : test =" + test); }} 运行结果:123Before test(boolean) : test = trueIn test(boolean) : test = falseAfter test(boolean) : test = true]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-反射(Reflection)]]></title>
<url>%2F2019%2F04%2F27%2FJava-Reflection%2F</url>
<content type="text"><![CDATA[在Java语言里面之所以会有如此众多的开源技术支撑,很大的一部分来自于Java最大的特征——反射机制,如果不能灵活的使用反射进行项目的开发与设计,那么可以说你未接触到Java的精髓。所有的技术实现的目标只有一点:重用性。对于反射机制首先考虑的是“反”与“正”的操作,所谓的“正”操作指的是当我们要使用一个类的时候,一定要先导入程序所在的包,而后根据类进行对象的实例化,并且依靠对象调用类中的方法。但是如果说“反”,根据实例化对象推出其类型。如果要实现反的处理操作,那么首先要采用的就是Object类中所提供的一个方法获取Class对象信息:public final Class<?>getClass();通过getClass()可以帮助使用者找到对象的根源 可以运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor类的newInstance()方法,创建一个对象1234567891011121314ipublic class Main { public static void main(String[] args) { Class<?> sr= null; try { sr = Class.forName("Person"); Object o = sr.getConstructor().newInstance(); System.out.println(o); } catch (Exception e) { e.printStackTrace(); } System.out.println(sr); }}]]></content>
<categories>
<category>Java高级</category>
</categories>
<tags>
<tag>Java高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-final, finally, finalize的区别]]></title>
<url>%2F2019%2F04%2F27%2FJava-final-finally-finalize%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[finalfinal 可以用于控制成员,方法,或者是一个类是否可以被重写或者继承等功能 finnal 成员当在类中定义变量时,若在前面加上final关键字,那么,这个变量一旦被初始化,便不可改变,这里的不可变的意思对基本类型来说其值不可变,而对于对象变量来说是其引用不可变,初始化可以在两个地方,一是其定义处,二是在构造函数中,两者只能选其一。当你在方法中不需要改变做为参数的对象变量时,明确使用final进行声明,会防止你无意地修改影响调用方法。 final 方法将方法声明为final有两个原因,第一就是明确已经知道这个方法提供的功能满足要求,不需要进行拓展,并且不允许任何从此类继承的类来重写这个方法,但是可以继承这个方法,即可以直接调用;第二就是允许编译器将所有对此方法的调用转化为inline(行内)调用的机制,它会在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如:保存断点,压栈等操作,这样可能会使程序效率有所提高。 final 类一个final 类是无法被任何类继承的,也就意味着,此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美,不需要进行修改或扩展。final类中的成员既可以明确指出final也可以不指出。 finalyfinally 关键字是对Java异常处理模型的补充,finally结构使代码总会执行,而不管有无异常发生,使用finally可以维护对象的内部状态,并且可以清理非内存资源。 finalizefinalize是方法名。Java允许使用finalize()方法在垃圾收集器将对象从内存清除出去之前作必要的清理工作。它是Object类中定义的,所有的类都继承了它。]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-异常]]></title>
<url>%2F2019%2F04%2F27%2FJava-%E5%BC%82%E5%B8%B8%2F</url>
<content type="text"><![CDATA[什么是异常在Java程序运行时,常常会出现一些非正常的现象,这种情况称为运行错误。根据其性质可以分为异常和错误。Java程序中,所有抛出的异常都必须从Throwable派生而来。类Throwable有两个直接子类:Error和Exception。一般来说,最常见的错误程序进入死循环,内存泄露等Java错误对应的类为Error类,Error类对象有Java虚拟机生成并抛弃Java异常对应的类为Exception类,Java编译器要求程序必须捕获或声明所有的非运行时异常,但对运行时异常可以不做处理。其中类RuntimeException代表运行时由Java虚拟机生成的异常,原因是编程错误,其他则为非运行异常,原因是程序碰到了意外情况,如输入/输出异常IOException等。 异常关键字Java异常处理的关键语句有五个:try, catch, throw, throws, finally. 其中,try, catch, finally三个语句块应注意:1)try, catch, finally 三个语句块不能单独使用三者可以组成try…catch…finally, try…catch, try…finally三种结构,catch语句可以有一个或者多个,finally语句最多一个。2)try, catch, finally 三个变量的作用域为代码块内部,分别独立而不能互相访问3)若有多个catch块,只会匹配其中一个异常类并执行catch块代码,而不会在执行别的catch块,并且匹配catch语句的顺序是由上到下的。 throw, throws关键字的区别如下throw 关键字用于方法体内部,用来抛出一个Throwable类型的异常。如果抛出了检查异常,则还应该在头部声明方法可能抛出的异常类型。throws 关键字用于方法体外部的声明部分,仅当抛出了检查异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应当继续抛出。 异常处理中常见的问题过于庞大的try块大量的语句装入一个try块,找出异常不容易;对于这种问题,可以设置多个异常抛出点来解决,异常对象从产生点产生后,到被捕捉后终止生命的全过程中,实际是一个传值过程 异常的完整性在Java语言中,如果一个函数运行时可能会向上层调用者函数抛出一个异常。那么,它就必须在该函数的声明中显式地注明(采用throws关键字)。如果你在程序中利用throw出现一个异常,那么你的程序中必须要用catch处理这个异常。1234567try{ //throw Exception}catch (Exception ex){ //find Exception //hand of it}]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-运算符]]></title>
<url>%2F2019%2F04%2F27%2FJava-%E8%BF%90%E7%AE%97%E7%AC%A6%2F</url>
<content type="text"><![CDATA[语言中运算符分为三类:单目运算符,二目运算符,三目运算符。单目运算符只对一个参数进行运算(如++,—-等);双目运算符就是对两个参数进行运算(如+,-,>,<,等);三目运算符就是对三个参数进行运算(如?,:)。运算符的优先级别(1级优先级最高,16级最低):123456789101112131415161级:—— . ()2级:—— ++ --3级:—— new 4级:—— * / %5级:—— + -6级:—— >> << >> >7级:—— > < >= <=8级:—— == !=9级:—— &10级:—— ^11级:—— !12级:—— &&13级:—— ||14级:—— ?:15级:—— = += -= /= %= ^=16级:—— &= <<= >>= 在Java编程规范中提到:当两个表达式有一个是常量表达式,另一个变量是T时,而常量表达式可以通过被T表示时,输出的结果是T类型。“&”, “|”, “^” 是位运算符并且可以充当布尔逻辑运算符。例:int c = a & b;意思是首先a和b按位与,a是1,b是2,a的二进制数位是0001,b的二进制位是0010,布尔运算符优先级别高于逻辑运算符。&,| 逻辑运算与 &&,|| 运算符的重要区别是,前者是非短路运算,后者是短路运算编译器对 && 和 || 已经优化过,凡 && 前面的是false,那么 && 后面的表达式将不会执行,|| 前面的是true,|| 后面的就不用做了。]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java 性能调优]]></title>
<url>%2F2019%2F04%2F23%2FJava-%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98%2F</url>
<content type="text"><![CDATA[程序的性能通过执行速度,内存分配,启动时间,负载承受能力来变现,其中:执行速度:程序的反映是否迅速,响应时间是否足够短。内存分配:内存分配是否合理,是否过多地消耗内存或者存在泄露。启动时间:程序从运行到可以正常处理业务需要花费多长时间。负载承受能力:当系统压力上升时,系统的执行速度,响应时间的上升曲线是否平缓。 性能的参考标准:执行时间:一段代码从开始运行到运行结束,所使用的时间CPU时间:函数或者线程所占用的CPU的时间内存分配:程序在运行时所占用的内存空间磁盘吞吐量:描述I/O使用情况网络吞吐量:描述网络的使用情况响应时间:系统对某用户行为或者事件做出响应的时间,响应时间鱼越短,性能越好]]></content>
<categories>
<category>Java高级</category>
</categories>
<tags>
<tag>Java高级</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-类型转换]]></title>
<url>%2F2019%2F04%2F15%2FJava-%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2%2F</url>
<content type="text"><![CDATA[Java 的数据类型分为三大类,即布尔型,字符型和数值型,其中数值型又分为整型和浮点型,相对于数据类型,Java的变量类型为boolean;字符型为char,整型byte、short、int、long;浮点型float、double其中四种整型变量和两种浮点型变量分别对应于不同的精度和范围。此外,编程时还经常用到两种类型变量,即String和Data。 数据类型转换的种类Java数据类型的转换一般分三种,分别是:简单数据类型之间的转换,字符串与其他数据类型之间的转换,其他实用数据类型的转换。 简单数据类型之间的转换在Java中,整型,实型,字符型被视为简单数据类型,这些类型由低级到高级分别为(byte, short,char)——>int——>long——>float——>double。简单数据类型之间的转换又可以分为:低级到高级的自动类型转换,高级到低级的强制类型转换,包装类过滤类型能够转换 自动类型转换低级变量可以直接转换为高级变量,这叫自动类型转换。例如,下面的语句可以在节Java中直接通过 byte b ; i = b ; long 1 = b ; float f = b ;double d = b ;如果低级类型为char型,向高级类型(整型)转换时,会转换为对应的ASCLL码值,例如; char c = 'c' ; int i = c ; System.out.println("output:" + i);输出 : output:99; 强制类型转换int i = 99 ; byte b = (byte)i ; char c = (char)i; 注意:这种转换可能会导致溢出或精度的下降。 包装类过滤类型转换Java 的包装类可以直接将简单类型的变量表示为一个类。Java共有六种包装类,分别是Boolean、Character、Integer、Long、Float和Double。String和Data本身就是类,不存在包装类的概念。在进行简单数据类型之间的抓换时,可以利用包装类进行中间过渡,一般情况下,首先声明一个变量,然后生成一个对应的包装类,就可以利用包装类的各种方法进行类型转换。例1:float——>double 123float f1 = 100.00f ; Float F1 = new Float(f1) ;Double d1 = F1.doubleValue() ; 例2:double——>int 123double d1 = 100.00;Double D1 = new Double(d1);int i1 = D1.intValue(D1); 例3:int——>double ,自动转换: 12int i1 = 200;double d1 = i1; 字符串与其他数据类型之间的转换在Java中,所有的类都继承于java.lang.Object, 在Object类中定义了toString()方法,即所有的类都可转换为字符串。 字符串转其他类型字符串转换为数值型变量有两种对应关系:一种是其转换为对应的ASCLL码;两一种是转换关系,例如,”1”就是数值1,而不是ASCLL码,对于这种转换,可以使用Character的getNumericValue(char ch)方法。]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>Java基础</tag>
</tags>
</entry>
</search>